| @@ -11,7 +11,7 @@ import Combat from './components/Combat.vue' | |||
| import Header from './components/Header.vue' | |||
| import * as Creatures from '@/game/creatures' | |||
| import * as Items from '@/game/items' | |||
| import { Creature, POV } from '@/game/entity' | |||
| import { Creature } from '@/game/entity' | |||
| import { ProperNoun, TheyPronouns, FemalePronouns, MalePronouns, ImproperNoun } from '@/game/language' | |||
| @Component({ | |||
| @@ -71,7 +71,8 @@ export default class App extends Vue { | |||
| const withers = new Creatures.Withers() | |||
| const kenzie = new Creatures.Kenzie() | |||
| this.combatants = [fighter, withers, wizard, rogue, cleric, kenzie] | |||
| const cafat = new Creatures.Cafat() | |||
| this.combatants = [fighter, withers, wizard, rogue, cleric, kenzie, cafat] | |||
| } | |||
| } | |||
| </script> | |||
| @@ -8,7 +8,8 @@ | |||
| <script lang="ts"> | |||
| 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 { Stats, Stat } from '@/game/combat' | |||
| import { Container } from '@/game/vore' | |||
| @@ -54,7 +54,8 @@ | |||
| <script lang="ts"> | |||
| 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 { Stats, Stat, StatIcons, StatDescs, Vigor, VigorIcons, VigorDescs, VoreStatDescs, VoreStatIcons } from '@/game/combat' | |||
| import ContainerView from './ContainerView.vue' | |||
| import tippy, { createSingleton } from 'tippy.js' | |||
| @@ -1,6 +1,6 @@ | |||
| import { StatTest, StatVigorTest } from './tests' | |||
| import { POVPairArgs, POVPair, DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '../language' | |||
| import { Entity, POV, Creature } from '../entity' | |||
| import { DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '../language' | |||
| import { Entity, Creature } from '../entity' | |||
| import { Damage, DamageFormula, Stat, Vigor, Action } from '../combat' | |||
| import { LogLine, LogLines, LogEntry, CompositeLog } from '../interface' | |||
| import { VoreContainer, Container } from '../vore' | |||
| @@ -85,8 +85,6 @@ export class DevourAction extends Action { | |||
| } | |||
| export class FeedAction extends Action { | |||
| private test: StatTest | |||
| protected successLine: PairLine<Entity> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('feed'))} ${user.pronouns.reflexive} to ${target.name}. ` | |||
| ) | |||
| @@ -114,7 +112,6 @@ export class FeedAction extends Action { | |||
| [new UserDrainedVigorCondition(Vigor.Resolve), new TogetherCondition()] | |||
| ) | |||
| this.name += ` (${container.name})` | |||
| this.test = new StatTest(Stat.Power) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| @@ -1,18 +1,18 @@ | |||
| import { Creature, POV } from '../entity' | |||
| import { LogEntry, LogLine, FAElem } from '../interface' | |||
| import { Effect } from '../combat' | |||
| import { POVSolo } from '../language' | |||
| import { SoloLine, ToBe } from '../language' | |||
| export class InstantKill implements Effect { | |||
| lines = new POVSolo<Creature>([ | |||
| [[POV.Second], (target: Creature) => new LogLine(`You're killed instantly! `, new FAElem('fas fa-skull'))], | |||
| [[POV.Third], (target: Creature) => new LogLine(`${target.name.capital} is killed instantly! `, new FAElem('fas fa-skull'))] | |||
| ]) | |||
| line: SoloLine<Creature> = (victim) => new LogLine( | |||
| `${victim.name.capital} ${victim.name.conjugate(new ToBe())} killed instantly!`, | |||
| new FAElem('fas fa-skull') | |||
| ) | |||
| name = "Instant Kill" | |||
| desc = "Instantly kills its victim" | |||
| apply (target: Creature): LogEntry { | |||
| target.vigors.Health = Math.min(0, target.vigors.Health) | |||
| return this.lines.run(target) | |||
| return this.line(target) | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import { Creature, POV, Entity } from '../entity' | |||
| import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula } from '../combat' | |||
| import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, POVPairArgs, Verb } from '../language' | |||
| import { Creature, Entity } from '../entity' | |||
| 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 { LogLine, LogLines, LogEntry, FAElem, CompositeLog, ImgElem } from '../interface' | |||
| import { AttackAction, EatenAction, TransferAction, FeedAction } from '../combat/actions' | |||
| @@ -91,6 +91,8 @@ export class Cafat extends Creature { | |||
| [Stat.Charm]: 20 | |||
| }, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 150) | |||
| this.side = Side.Monsters | |||
| const stomach = new Stomach(this, 100, new Damage( | |||
| { amount: 20, type: DamageType.Acid, target: Vigor.Health }, | |||
| { amount: 10, type: DamageType.Crush, target: Vigor.Stamina }, | |||
| @@ -1,10 +1,8 @@ | |||
| import { Creature, POV } from '../entity' | |||
| import { Creature } from '../entity' | |||
| import { ProperNoun, ImproperNoun, FemalePronouns, Verb } from '../language' | |||
| import { VoreType, Stomach, Vore } from '../vore' | |||
| import { Side, Damage, DamageType, Vigor, UniformRandomDamageFormula, ConstantDamageFormula, StatDamageFormula, Stat, VoreStat } from '../combat' | |||
| import { LogLine } from '../interface' | |||
| import { FeedAction, TransferAction, AttackAction } from '../combat/actions' | |||
| import * as Words from '../words' | |||
| import { VoreType, Stomach } from '../vore' | |||
| import { Side, Damage, DamageType, Vigor, StatDamageFormula, Stat, VoreStat } from '../combat' | |||
| import { AttackAction } from '../combat/actions' | |||
| export class Kenzie extends Creature { | |||
| title = "Large Lycanroc" | |||
| @@ -1,5 +1,5 @@ | |||
| import { Creature, POV } from '../entity' | |||
| import { ProperNoun, TheyPronouns, ImproperNoun } from '../language' | |||
| import { Creature } from '../entity' | |||
| import { ProperNoun, TheyPronouns, ImproperNoun, POV } from '../language' | |||
| import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula } from '../combat' | |||
| import { Stomach, Bowels, VoreType } from '../vore' | |||
| import { AttackAction } from '../combat/actions' | |||
| @@ -1,6 +1,6 @@ | |||
| import { Creature, POV } from '../entity' | |||
| import { Creature } from '../entity' | |||
| 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 } from '../language' | |||
| import { ImproperNoun, POVPair, POVPairArgs, ProperNoun, FemalePronouns, RandomWord, Adjective, Verb, POV } from '../language' | |||
| import { LogLine, LogLines, LogEntry, Newline } from '../interface' | |||
| import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container } from '../vore' | |||
| import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions' | |||
| @@ -1,6 +1,6 @@ | |||
| import { Creature, POV, Entity } from '../entity' | |||
| import { Creature, Entity } from '../entity' | |||
| import { Stat, Damage, DamageType, ConstantDamageFormula, Vigor } from '../combat' | |||
| import { MalePronouns, ImproperNoun, POVPair, POVPairArgs } from '../language' | |||
| import { MalePronouns, ImproperNoun, POVPair, POVPairArgs, POV } from '../language' | |||
| import { LogLine, LogLines } from '../interface' | |||
| import { VoreType, Stomach, Bowels } from '../vore' | |||
| import { StatTest } from '../combat/tests' | |||
| @@ -1,11 +1,9 @@ | |||
| import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat, Side, GroupAction, Vigors } from './combat' | |||
| import { Noun, Pronoun, Adjective, ImproperNoun, TextLike } from './language' | |||
| import { Noun, Pronoun, TextLike, POV } from './language' | |||
| import { LogEntry, LogLine } from './interface' | |||
| import { Vore, VoreContainer, VoreType, Container } from './vore' | |||
| import { Item } from './items' | |||
| export enum POV {First, Second, Third} | |||
| export interface Entity { | |||
| name: Noun; | |||
| pronouns: Pronoun; | |||
| @@ -1,6 +1,8 @@ | |||
| import { Entity, POV } from './entity' | |||
| import { Entity } from './entity' | |||
| import { LogEntry, LogLine } from './interface' | |||
| export enum POV {First, Second, Third} | |||
| export type SoloLine<T> = (user: T) => LogEntry | |||
| export type SoloLineArgs<T, V> = (user: T, args: V) => LogEntry | |||
| export type PairLine<T> = (user: T, target: T) => LogEntry | |||
| @@ -88,6 +90,7 @@ interface WordOptions { | |||
| count: boolean; | |||
| possessive: boolean; | |||
| objective: boolean; | |||
| perspective: POV; | |||
| } | |||
| const emptyConfig: WordOptions = { | |||
| @@ -100,7 +103,8 @@ const emptyConfig: WordOptions = { | |||
| proper: false, | |||
| vowel: VowelSound.Default, | |||
| possessive: false, | |||
| objective: false | |||
| objective: false, | |||
| perspective: POV.Third | |||
| } | |||
| export type TextLike = { toString: () => string } | |||
| @@ -233,6 +237,24 @@ export abstract class Word { | |||
| opts.objective = true | |||
| return this.configure(opts) as this | |||
| } | |||
| get pov1 (): this { | |||
| const opts: WordOptions = Object.assign({}, this.opt) | |||
| opts.perspective = POV.First | |||
| return this.configure(opts) as this | |||
| } | |||
| get pov2 (): this { | |||
| const opts: WordOptions = Object.assign({}, this.opt) | |||
| opts.perspective = POV.Second | |||
| return this.configure(opts) as this | |||
| } | |||
| get pov3 (): this { | |||
| const opts: WordOptions = Object.assign({}, this.opt) | |||
| opts.perspective = POV.Third | |||
| return this.configure(opts) as this | |||
| } | |||
| } | |||
| export class RandomWord extends Word { | |||
| @@ -329,13 +351,13 @@ export class Noun extends Word { | |||
| export class ImproperNoun extends Noun { | |||
| constructor (singularNoun: string, pluralNoun: string = singularNoun, possessiveNoun: string = singularNoun + "'s") { | |||
| super(singularNoun, pluralNoun, null, { plural: false, allCaps: false, capital: false, proper: false, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false }) | |||
| super(singularNoun, pluralNoun, null, { plural: false, allCaps: false, capital: false, proper: false, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false, perspective: POV.Third }) | |||
| } | |||
| } | |||
| export class ProperNoun extends Noun { | |||
| constructor (singularNoun: string) { | |||
| super(singularNoun, null, null, { plural: false, allCaps: false, capital: false, proper: true, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false }) | |||
| super(singularNoun, null, null, { plural: false, allCaps: false, capital: false, proper: true, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false, perspective: POV.Third }) | |||
| } | |||
| } | |||
| @@ -391,6 +413,38 @@ export class Verb extends Word { | |||
| } | |||
| } | |||
| // this one is obnoxious | |||
| export class ToBe extends Word { | |||
| configure (opts: WordOptions): Word { | |||
| return new ToBe(opts) | |||
| } | |||
| toString (): string { | |||
| let choice | |||
| if (this.opts.plural) { | |||
| choice = 'are' | |||
| } | |||
| switch (this.opts.perspective) { | |||
| case POV.First: choice = 'am'; break | |||
| case POV.Second: choice = 'are'; break | |||
| case POV.Third: choice = 'is'; break | |||
| } | |||
| if (this.opt.allCaps) { | |||
| choice = choice.toUpperCase() | |||
| } else if (this.opt.capital) { | |||
| choice = choice.slice(0, 1).toUpperCase() + choice.slice(1) | |||
| } | |||
| return choice | |||
| } | |||
| constructor (protected opts: WordOptions = emptyConfig) { | |||
| super(opts) | |||
| } | |||
| } | |||
| interface PronounDict { | |||
| subjective: string; | |||
| objective: string; | |||
| @@ -432,26 +486,6 @@ export class Pronoun implements Pluralizable { | |||
| } | |||
| } | |||
| export class PronounAsNoun extends Noun { | |||
| constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) { | |||
| super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt) | |||
| this.options.nounKind = NounKind.All | |||
| this.options.plural = true | |||
| } | |||
| configure (opts: WordOptions): Word { | |||
| return new PronounAsNoun(this.pronouns, opts) | |||
| } | |||
| toString (): string { | |||
| if (this.options.objective) { | |||
| return new Noun(this.pronouns.objective, this.pronouns.objective, this.pronouns.possessive, this.options).toString() | |||
| } else { | |||
| return super.toString() | |||
| } | |||
| } | |||
| } | |||
| export const MalePronouns = new Pronoun({ | |||
| subjective: 'he', | |||
| objective: 'him', | |||
| @@ -500,3 +534,33 @@ export const FirstPersonPronouns = new Pronoun({ | |||
| possessive: 'my', | |||
| reflexive: 'myself' | |||
| }) | |||
| export class PronounAsNoun extends Noun { | |||
| constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) { | |||
| super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt) | |||
| this.options.nounKind = NounKind.All | |||
| this.options.plural = true | |||
| } | |||
| configure (opts: WordOptions): Word { | |||
| return new PronounAsNoun(this.pronouns, opts) | |||
| } | |||
| toString (): string { | |||
| if (this.options.objective) { | |||
| return new Noun(this.pronouns.objective, this.pronouns.objective, this.pronouns.possessive, this.options).toString() | |||
| } else { | |||
| return super.toString() | |||
| } | |||
| } | |||
| conjugate (verb: Word): Word { | |||
| if (this.pronouns === FirstPersonPronouns) { | |||
| return super.conjugate(verb.pov1) | |||
| } else if (this.pronouns === SecondPersonPronouns) { | |||
| return super.conjugate(verb.pov2) | |||
| } else { | |||
| return super.conjugate(verb.pov3) | |||
| } | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| import { Mortal, POV } from './entity' | |||
| import { Mortal } from './entity' | |||
| import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats } from './combat' | |||
| import { LogLines, LogEntry, LogLine } from './interface' | |||
| import { Noun, Pronoun, ImproperNoun, POVSolo, TextLike, Verb, SecondPersonPronouns, PronounAsNoun, FirstPersonPronouns, PairLineArgs } from './language' | |||
| import { Noun, Pronoun, ImproperNoun, TextLike, Verb, SecondPersonPronouns, PronounAsNoun, FirstPersonPronouns, PairLineArgs, SoloLine, POV } from './language' | |||
| import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions' | |||
| import * as Words from './words' | |||
| @@ -56,10 +56,9 @@ export abstract class Vore implements Mortal { | |||
| abstract containers: Array<VoreContainer>; | |||
| abstract otherContainers: Array<Container>; | |||
| destroy (): LogEntry { | |||
| const lines = new POVSolo<Vore>([ | |||
| [[POV.Second], () => new LogLine('You die!')], | |||
| [[POV.Third], (target: Vore) => new LogLine(`${target.name.capital} dies!`)] | |||
| ]) | |||
| const line: SoloLine<Vore> = (victim) => new LogLine( | |||
| `${victim.name.capital} ${victim.name.conjugate(new Verb('die'))}` | |||
| ) | |||
| const released: Array<Vore> = this.containers.flatMap(container => { | |||
| return container.contents.map(prey => { | |||
| @@ -79,17 +78,17 @@ export abstract class Vore implements Mortal { | |||
| if (released.length > 0) { | |||
| if (this.containedIn === null) { | |||
| return new LogLines( | |||
| lines.run(this), | |||
| line(this), | |||
| new LogLine(names + ` spill out!`) | |||
| ) | |||
| } else { | |||
| return new LogLines( | |||
| lines.run(this), | |||
| line(this), | |||
| new LogLine(names + ` spill out into ${this.containedIn.owner.name}'s ${this.containedIn.name}!`) | |||
| ) | |||
| } | |||
| } else { | |||
| return lines.run(this) | |||
| return line(this) | |||
| } | |||
| } | |||
| } | |||