| @@ -72,7 +72,8 @@ export default class App extends Vue { | |||||
| const withers = new Creatures.Withers() | const withers = new Creatures.Withers() | ||||
| const kenzie = new Creatures.Kenzie() | const kenzie = new Creatures.Kenzie() | ||||
| const cafat = new Creatures.Cafat() | const cafat = new Creatures.Cafat() | ||||
| this.combatants = [fighter, withers, wizard, rogue, cleric, kenzie, cafat] | |||||
| const wolf = new Creatures.Wolf() | |||||
| this.combatants = [fighter, withers, wizard, rogue, cleric, kenzie, cafat, wolf] | |||||
| } | } | ||||
| } | } | ||||
| </script> | </script> | ||||
| @@ -42,7 +42,8 @@ | |||||
| <script lang="ts"> | <script lang="ts"> | ||||
| import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | ||||
| import { Creature, POV } from '@/game/entity' | |||||
| import { Creature } from '@/game/entity' | |||||
| import { POV } from '@/game/language' | |||||
| import { LogEntry } from '@/game/interface' | import { LogEntry } from '@/game/interface' | ||||
| import Statblock from './Statblock.vue' | import Statblock from './Statblock.vue' | ||||
| import ActionButton from './ActionButton.vue' | import ActionButton from './ActionButton.vue' | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { Creature, POV } from '../entity' | |||||
| import { Creature } from '../entity' | |||||
| import { LogEntry, LogLine, FAElem } from '../interface' | import { LogEntry, LogLine, FAElem } from '../interface' | ||||
| import { Effect } from '../combat' | import { Effect } from '../combat' | ||||
| import { SoloLine, ToBe } from '../language' | import { SoloLine, ToBe } from '../language' | ||||
| @@ -1,26 +1,17 @@ | |||||
| import { Creature, Entity } from '../entity' | import { Creature, Entity } from '../entity' | ||||
| import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula, Side } from '../combat' | import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula, Side } from '../combat' | ||||
| import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, POVPairArgs, Verb, POV } from '../language' | |||||
| import { VoreType, Stomach, InnerStomach, VoreContainer, NormalContainer, Vore } from '../vore' | |||||
| import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, Verb, POV, PairLineArgs, PairLine } from '../language' | |||||
| import { VoreType, Stomach, InnerStomach, VoreContainer } from '../vore' | |||||
| import { LogLine, LogLines, LogEntry, FAElem, CompositeLog, ImgElem } from '../interface' | import { LogLine, LogLines, LogEntry, FAElem, CompositeLog, ImgElem } from '../interface' | ||||
| import { AttackAction, EatenAction, TransferAction, FeedAction } from '../combat/actions' | import { AttackAction, EatenAction, TransferAction, FeedAction } from '../combat/actions' | ||||
| import { InstantKill } from '../combat/effects' | import { InstantKill } from '../combat/effects' | ||||
| import * as Words from '../words' | |||||
| class BellyCrushAction extends AttackAction { | class BellyCrushAction extends AttackAction { | ||||
| successLines = new POVPairArgs<Entity, Entity, { damage: Damage }>([ | |||||
| [[POV.Second, POV.Third], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `You crush on ${target.name} with your belly for `, | |||||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines(new LogLine( | |||||
| `${user.name.capital} ${user.name.conjugate(new Verb('crush', 'crushes'))} on ${target.name.objective} with ${user.pronouns.possessive} belly for `, | |||||
| args.damage.renderShort() | args.damage.renderShort() | ||||
| ), new ImgElem('./media/cafat/images/belly-crush.webp'))], | |||||
| [[POV.Third, POV.Second], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `${user.name.capital} crushes on you with ${user.pronouns.possessive} belly for `, | |||||
| args.damage.renderShort() | |||||
| ), new ImgElem('./media/cafat/images/belly-crush.webp'))], | |||||
| [[POV.Third, POV.Third], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `${user.name.capital} crushes on ${target.name} with ${user.pronouns.possessive} belly for `, | |||||
| args.damage.renderShort() | |||||
| ), new ImgElem('./media/cafat/images/belly-crush.webp'))] | |||||
| ]) | |||||
| ), new ImgElem('./media/cafat/images/belly-crush.webp')) | |||||
| constructor (_damage: Damage) { | constructor (_damage: Damage) { | ||||
| super({ | super({ | ||||
| @@ -38,20 +29,13 @@ class BellyCrushAction extends AttackAction { | |||||
| } | } | ||||
| class BelchAction extends AttackAction { | class BelchAction extends AttackAction { | ||||
| successLines = new POVPairArgs<Entity, Entity, { damage: Damage }>([ | |||||
| [[POV.Second, POV.Third], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `You belch on ${target.name} for `, | |||||
| args.damage.renderShort() | |||||
| ), new ImgElem('./media/cafat/images/belch.webp'))], | |||||
| [[POV.Third, POV.Second], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `${user.name.capital} belches on you for `, | |||||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines( | |||||
| new LogLine( | |||||
| `${user.name.capital} ${user.name.conjugate} on ${target.name.objective} for `, | |||||
| args.damage.renderShort() | args.damage.renderShort() | ||||
| ), new ImgElem('./media/cafat/images/belch.webp'))], | |||||
| [[POV.Third, POV.Third], (user, target, args) => new CompositeLog(new LogLine( | |||||
| `${user.name.capital} belches on ${target.name} for `, | |||||
| args.damage.renderShort() | |||||
| ), new ImgElem('./media/cafat/images/belch.webp'))] | |||||
| ]) | |||||
| ), | |||||
| new ImgElem('./media/cafat/images/belch.webp') | |||||
| ) | |||||
| constructor (damage: Damage) { | constructor (damage: Damage) { | ||||
| super(new ConstantDamageFormula(damage)) | super(new ConstantDamageFormula(damage)) | ||||
| @@ -61,19 +45,18 @@ class BelchAction extends AttackAction { | |||||
| } | } | ||||
| class CrushAction extends EatenAction { | class CrushAction extends EatenAction { | ||||
| lines: POVPair<Entity, Entity> = new POVPair([ | |||||
| [[POV.Second, POV.Third], (user, target) => new LogLine(`You crush ${target.name} `)], | |||||
| [[POV.Third, POV.Second], (user) => new CompositeLog(new LogLine(`${user.name.capital} crushes you; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `), new ImgElem('./media/cafat/images/crunch.webp'))], | |||||
| [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} crushes ${target.name}; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, new ImgElem('./media/cafat/images/crunch.webp'))] | |||||
| ]) | |||||
| constructor (container: VoreContainer) { | |||||
| super(container, "Crush", "Crush 'em!") | |||||
| line: PairLineArgs<Creature, { container: VoreContainer }> = (user, target, args) => new LogLine( | |||||
| `${user.name.capital.possessive} ${args.container.name} ${Words.Brutally} ${user.name.conjugate(new Verb('crush', 'crushes'))} ${target.name.objective}; ${user.pronouns.subjective} ${user.pronouns.conjugate(new Verb('belch', 'belches'))} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, | |||||
| new ImgElem('./media/cafat/images/crunch.webp') | |||||
| ) | |||||
| constructor (private _container: VoreContainer) { | |||||
| super(_container, "Crush", "Crush 'em!") | |||||
| this.desc = "Crush somebody in your gut" | this.desc = "Crush somebody in your gut" | ||||
| } | } | ||||
| execute (user: Creature, target: Creature): LogEntry { | execute (user: Creature, target: Creature): LogEntry { | ||||
| return new LogLines(this.lines.run(user, target), new InstantKill().apply(target)) | |||||
| return new LogLines(this.line(user, target, { container: this._container }), new InstantKill().apply(target)) | |||||
| } | } | ||||
| describe (user: Creature, target: Creature): LogEntry { | describe (user: Creature, target: Creature): LogEntry { | ||||
| @@ -1,6 +1,6 @@ | |||||
| import { Creature } from '../entity' | import { Creature } from '../entity' | ||||
| import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction, CombatTest, Stat, DamageFormula, UniformRandomDamageFormula, Action, DamageInstance, StatDamageFormula, VoreStat } from '../combat' | import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction, CombatTest, Stat, DamageFormula, UniformRandomDamageFormula, Action, DamageInstance, StatDamageFormula, VoreStat } from '../combat' | ||||
| import { ImproperNoun, POVPair, POVPairArgs, ProperNoun, FemalePronouns, RandomWord, Adjective, Verb, POV } from '../language' | |||||
| import { ImproperNoun, POVPair, ProperNoun, FemalePronouns, RandomWord, Adjective, Verb, POV } from '../language' | |||||
| import { LogLine, LogLines, LogEntry, Newline } from '../interface' | import { LogLine, LogLines, LogEntry, Newline } from '../interface' | ||||
| import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container } from '../vore' | import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container } from '../vore' | ||||
| import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions' | import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions' | ||||
| @@ -1,52 +1,23 @@ | |||||
| import { Creature, Entity } from '../entity' | |||||
| import { Stat, Damage, DamageType, ConstantDamageFormula, Vigor } from '../combat' | |||||
| import { MalePronouns, ImproperNoun, POVPair, POVPairArgs, POV } from '../language' | |||||
| import { LogLine, LogLines } from '../interface' | |||||
| import { Creature } from '../entity' | |||||
| import { Damage, DamageType, ConstantDamageFormula, Vigor, Side } from '../combat' | |||||
| import { MalePronouns, ImproperNoun } from '../language' | |||||
| import { VoreType, Stomach, Bowels } from '../vore' | import { VoreType, Stomach, Bowels } from '../vore' | ||||
| import { StatTest } from '../combat/tests' | |||||
| import { AttackAction, TransferAction, FeedAction } from '../combat/actions' | import { AttackAction, TransferAction, FeedAction } from '../combat/actions' | ||||
| class BiteAction extends AttackAction { | |||||
| constructor () { | |||||
| super(new ConstantDamageFormula(new Damage({ amount: 10, type: DamageType.Slash, target: Vigor.Health }))) | |||||
| this.name = "Bite" | |||||
| } | |||||
| } | |||||
| class HypnoAction extends AttackAction { | |||||
| protected successLines: POVPairArgs<Entity, Entity, { damage: Damage }> = new POVPairArgs([ | |||||
| [[POV.Second, POV.Third], (user, target, args) => new LogLine( | |||||
| `You hypnotize ${target.name} for `, | |||||
| args.damage.renderShort() | |||||
| )], | |||||
| [[POV.Third, POV.Second], (user, target, args) => new LogLine( | |||||
| `${user.name.capital} hypnotizes you for `, | |||||
| args.damage.renderShort() | |||||
| )], | |||||
| [[POV.Third, POV.Third], (user, target, args) => new LogLine( | |||||
| `${user.name.capital} hypnotizes ${target.name} for `, | |||||
| args.damage.renderShort() | |||||
| )] | |||||
| ]) | |||||
| protected failLines: POVPair<Entity, Entity> = new POVPair([ | |||||
| [[POV.Second, POV.Third], (user, target) => new LogLine(`You try to hypnotize ${target.name}, but you miss`)], | |||||
| [[POV.Third, POV.Second], (user, target) => new LogLine(`${user.name.capital} misses you`)], | |||||
| [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name} misses ${target.name}`)] | |||||
| ]) | |||||
| constructor () { | |||||
| super(new ConstantDamageFormula(new Damage({ amount: 30, type: DamageType.Dominance, target: Vigor.Resolve }))) | |||||
| this.test = new StatTest(Stat.Willpower) | |||||
| this.name = "Hypnotize" | |||||
| } | |||||
| } | |||||
| export class Wolf extends Creature { | export class Wolf extends Creature { | ||||
| constructor () { | constructor () { | ||||
| super(new ImproperNoun('wolf', 'wolves'), new ImproperNoun('wolf', 'wolves'), MalePronouns, { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 25) | super(new ImproperNoun('wolf', 'wolves'), new ImproperNoun('wolf', 'wolves'), MalePronouns, { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 25) | ||||
| this.actions.push(new BiteAction()) | |||||
| this.actions.push(new HypnoAction()) | |||||
| this.actions.push( | |||||
| new AttackAction( | |||||
| new ConstantDamageFormula( | |||||
| new Damage( | |||||
| { amount: 20, type: DamageType.Pierce, target: Vigor.Health } | |||||
| ) | |||||
| ) | |||||
| ) | |||||
| ) | |||||
| this.side = Side.Monsters | |||||
| const stomach = new Stomach(this, 50, new Damage( | const stomach = new Stomach(this, 50, new Damage( | ||||
| { amount: 20, type: DamageType.Acid, target: Vigor.Health }, | { amount: 20, type: DamageType.Acid, target: Vigor.Health }, | ||||
| @@ -24,37 +24,6 @@ export class POVPair<K extends Entity, V extends Entity> { | |||||
| } | } | ||||
| } | } | ||||
| export class POVPairArgs<K extends Entity, V extends Entity, U> { | |||||
| run (user: K, target: V, args: U): LogEntry { | |||||
| const choice = this.options.find(element => element[0][0] === user.perspective && element[0][1] === target.perspective) | |||||
| if (choice === undefined) { | |||||
| return new LogLine("Fen didn't write any text for this...") | |||||
| } else { | |||||
| return choice[1](user, target, args) | |||||
| } | |||||
| } | |||||
| constructor (private options: Array<[[POV, POV], (user: K, target: V, args: U) => LogEntry]>) { | |||||
| } | |||||
| } | |||||
| export class POVSolo<K extends Entity> { | |||||
| run (user: K): LogEntry { | |||||
| const choice = this.options.find(element => element[0][0] === user.perspective) | |||||
| if (choice === undefined) { | |||||
| return new LogLine("Fen didn't write any text for this...") | |||||
| } else { | |||||
| return choice[1](user) | |||||
| } | |||||
| } | |||||
| constructor (private options: Array<[[POV], (user: K) => LogEntry]>) { | |||||
| } | |||||
| } | |||||
| enum NounKind { | enum NounKind { | ||||
| Specific, | Specific, | ||||
| Nonspecific, | Nonspecific, | ||||
| @@ -477,6 +446,14 @@ export class Pronoun implements Pluralizable { | |||||
| return this.caps(this.pronouns.reflexive) | return this.caps(this.pronouns.reflexive) | ||||
| } | } | ||||
| conjugate (verb: Word): Word { | |||||
| if (this.isPlural) { | |||||
| return verb.root | |||||
| } else { | |||||
| return verb.singular | |||||
| } | |||||
| } | |||||
| private caps (input: string): string { | private caps (input: string): string { | ||||
| if (this.capitalize) { | if (this.capitalize) { | ||||
| return input.slice(0, 1).toUpperCase() + input.slice(1) | return input.slice(0, 1).toUpperCase() + input.slice(1) | ||||
| @@ -42,3 +42,12 @@ export const Bulge = new RandomWord([ | |||||
| new ImproperNoun("outline", "outlines"), | new ImproperNoun("outline", "outlines"), | ||||
| new ImproperNoun("imprint", "imprints") | new ImproperNoun("imprint", "imprints") | ||||
| ]) | ]) | ||||
| export const Brutally = new RandomWord([ | |||||
| new Adjective('brutally'), | |||||
| new Adjective('viciously'), | |||||
| new Adjective('callously'), | |||||
| new Adjective('savagely'), | |||||
| new Adjective('ruthlessly'), | |||||
| new Adjective('mercilessly') | |||||
| ]) | |||||