diff --git a/src/game/combat.ts b/src/game/combat.ts index 2d3e6ae..9597c83 100644 --- a/src/game/combat.ts +++ b/src/game/combat.ts @@ -1,6 +1,6 @@ import { Creature, POV, Entity } from './entity' import { POVPair, POVPairArgs, TextLike, DynText, LiveText } from './language' -import { Container } from './vore' +import { VoreContainer } from './vore' import { LogEntry, LogLines, CompositeLog, FAElem, LogLine, FormatEntry, FormatOpt, PropElem } from './interface' import { StatTest, StatVigorTest } from './combat/tests' diff --git a/src/game/combat/actions.ts b/src/game/combat/actions.ts index 9a9e335..b75fb45 100644 --- a/src/game/combat/actions.ts +++ b/src/game/combat/actions.ts @@ -3,7 +3,7 @@ import { POVPairArgs, POVPair, DynText, LiveText, TextLike } from '../language' import { Entity, POV, Creature } from '../entity' import { Damage, DamageFormula, Stat, Vigor, TogetherAction, PairAction, SelfAction } from '../combat' import { LogLine, LogLines, LogEntry, CompositeLog } from '../interface' -import { Container } from '../vore' +import { VoreContainer, Container } from '../vore' import { CapableCondition, DrainedVigorCondition } from './conditions' export class AttackAction extends TogetherAction { @@ -73,7 +73,7 @@ export class DevourAction extends TogetherAction { } constructor (protected container: Container) { - super(new DynText('Devour (', new LiveText(container, x => x.name.all), ')'), 'Try to consume your foe', [new CapableCondition()]) + super(new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'), 'Try to consume your foe', [new CapableCondition()]) this.test = new StatVigorTest(Stat.Power) } @@ -132,7 +132,7 @@ export class StruggleAction extends PairAction { protected failLines: POVPair = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You fail to escape from ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} tries to escape from you, but fails`)], - [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} unsuccessfully struggles within ${target.name}`)] + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} unsuccessfully struggles against ${target.name}`)] ]) allowed (user: Creature, target: Creature) { @@ -176,7 +176,7 @@ export abstract class EatenAction extends PairAction { } } - constructor (public container: Container, name: TextLike, desc: string) { + constructor (public container: VoreContainer, name: TextLike, desc: string) { super(new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), desc, [new CapableCondition()]) } } @@ -191,7 +191,7 @@ export class DigestAction extends SelfAction { } } - constructor (protected container: Container) { + constructor (protected container: VoreContainer) { super(new DynText('Digest (', new LiveText(container, container => container.name.all), ')'), 'Digest your prey', [new CapableCondition()]) } @@ -231,7 +231,7 @@ export class TransferAction extends PairAction { lines: POVPairArgs = new POVPairArgs([ [[POV.First, POV.Third], (user, target, args) => new LogLine(`You squeeze ${target.name} from your ${args.from.name} to your ${args.to.name}`)], [[POV.Third, POV.First], (user, target, args) => new LogLine(`You're squeezed from ${user.name}'s ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)], - [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name} squeezes ${target.name} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)] + [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name} squeezes ${target.name} from ${user.pronouns.possessive} ${args.from.name.all} to ${user.pronouns.possessive} ${args.to.name.all}`)] ]) allowed (user: Creature, target: Creature) { @@ -242,8 +242,8 @@ export class TransferAction extends PairAction { } } - constructor (protected from: Container, protected to: Container) { - super('Transfer', `Move from your ${from.name} to your ${to.name}`, [new CapableCondition()]) + constructor (protected from: Container, protected to: Container, name = 'Transfer') { + super(name, `${from.name.all.capital} to ${to.name.all}`, [new CapableCondition()]) } execute (user: Creature, target: Creature): LogEntry { diff --git a/src/game/creatures/cafat.ts b/src/game/creatures/cafat.ts index e6c500d..8d98270 100644 --- a/src/game/creatures/cafat.ts +++ b/src/game/creatures/cafat.ts @@ -1,7 +1,7 @@ import { Creature, POV, Entity } from '../entity' import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula } from '../combat' import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, POVPairArgs } from '../language' -import { VoreType, Stomach, InnerStomach, Container } from '../vore' +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' import { InstantKill } from '../combat/effects' @@ -66,7 +66,7 @@ class CrushAction extends EatenAction { [[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 `)] ]) - constructor (container: Container) { + constructor (container: VoreContainer) { super(container, "Crush", "Crush 'em!") this.desc = "Crush somebody in your gut" } diff --git a/src/game/creatures/withers.ts b/src/game/creatures/withers.ts index 912558f..9b68ce8 100644 --- a/src/game/creatures/withers.ts +++ b/src/game/creatures/withers.ts @@ -1,13 +1,43 @@ import { Creature, POV } from '../entity' import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction } from '../combat' -import { ImproperNoun, POVPair, ProperNoun, FemalePronouns, RandomWord, Adjective, POVPairArgs, POVSoloArgs } from '../language' +import { ImproperNoun, POVPair, ProperNoun, FemalePronouns, RandomWord, Adjective, POVPairArgs, POVSoloArgs, Verb } from '../language' import { LogLine, LogLines, LogEntry, Newline } from '../interface' -import { VoreType, Stomach, Container, Vore } from '../vore' -import { AttackAction, FeedAction } from '../combat/actions' +import { VoreType, Stomach, VoreContainer, Vore, NormalContainer } from '../vore' +import { AttackAction, FeedAction, TransferAction } from '../combat/actions' import { TogetherCondition } from '../combat/conditions' import { InstantKill } from '../combat/effects' import * as Words from '../words' +class MawContainer extends NormalContainer { + consumeVerb = new Verb('grab') + releaseVerb = new Verb('release') + struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled') + + consumeLines = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You snatch ${target.name} up in your jaws`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} snatches you up in ${user.pronouns.possessive} maw`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} snatches ${target.name} up in ${user.pronouns.possessive} maw`)] + ]) + + releaseLines = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You let out ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} lets you out `)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} lets out ${target.name}`)] + ]) + + struggleLines = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way free of ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way free!`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from ${target.name}`)] + ]) + + constructor (owner: Vore, stomach: VoreContainer) { + super(new ImproperNoun('maw'), owner, new Set([VoreType.Oral]), 50) + + this.actions.push(new TransferAction(this, stomach)) + } +} + const huge = new RandomWord([ new Adjective('massive'), new Adjective('colossal'), @@ -84,7 +114,7 @@ class DevourAllAction extends GroupAction { return new LogLine('Eat all ', targets.length.toString(), ' of \'em!') } - constructor (private container: Container) { + constructor (private container: VoreContainer) { super('Devour All', 'GULP!', [ new TogetherCondition() ]) @@ -122,12 +152,16 @@ export class Withers extends Creature { stomach.digestLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach ${Words.Digests.singular} ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach ${Words.Digests.singular} you`)], - [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade as the ${target.kind.all} is ${Words.Digests.past} by the ${user.kind}'s ${stomach.name}.`)] + [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s ${Words.Struggles.present} fades as the ${target.kind.all} is ${Words.Digests.past} by the ${user.kind.all}'s ${stomach.name}.`)] ]) this.containers.push(stomach) this.otherActions.push(new FeedAction(stomach)) this.groupActions.push(new DevourAllAction(stomach)) + + const grapple = new MawContainer(this, stomach) + + this.otherContainers.push(grapple) } } diff --git a/src/game/entity.ts b/src/game/entity.ts index b246d26..e524e84 100644 --- a/src/game/entity.ts +++ b/src/game/entity.ts @@ -1,7 +1,7 @@ import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat, Side, GroupAction } from './combat' import { Noun, Pronoun, Adjective, ImproperNoun } from './language' import { LogEntry, LogLine } from './interface' -import { Vore, Container, VoreType } from './vore' +import { Vore, VoreContainer, VoreType, Container } from './vore' export enum POV {First, Third} @@ -47,16 +47,17 @@ export class Creature extends Vore implements Combatant { resistances: Map = new Map() perspective: POV = POV.Third - containers: Array = [] - actions: Array = []; - groupActions: Array = []; - otherActions: Array = []; + containers: Array = [] + otherContainers: Array = [] + actions: Array = [] + groupActions: Array = [] + otherActions: Array = [] get bulk (): number { return this.voreStats.Mass + this.containers.reduce((total, conatiner) => { return total + conatiner.contents.reduce((total, prey) => total + prey.voreStats.Bulk, 0) }, 0) } - containedIn: Container|null = null; + containedIn: VoreContainer|null = null; constructor (public name: Noun, public kind: Noun, public pronouns: Pronoun, public stats: Stats, public preyPrefs: Set, public predPrefs: Set, mass: number) { super() @@ -67,7 +68,7 @@ export class Creature extends Vore implements Combatant { get [VoreStat.Bulk] () { console.log(containers) return containers.reduce( - (total: number, container: Container) => { + (total: number, container: VoreContainer) => { return total + container.contents.reduce( (total: number, prey: Vore) => { return total + prey.voreStats.Bulk @@ -86,7 +87,7 @@ export class Creature extends Vore implements Combatant { [VoreStat.Mass]: mass, get [VoreStat.PreyCount] () { return containers.reduce( - (total: number, container: Container) => { + (total: number, container: VoreContainer) => { return total + container.contents.reduce( (total: number, prey: Vore) => { return total + 1 + prey.voreStats[VoreStat.PreyCount] @@ -140,14 +141,19 @@ export class Creature extends Vore implements Combatant { return "Broken" } if (this.containedIn !== null) { - return `Devoured by ${this.containedIn.owner.name}` + return `${this.containedIn.consumeVerb.past.capital} by ${this.containedIn.owner.name}` } return "Normal" } validActions (target: Creature): Array { - let choices = this.actions.concat(this.containers.flatMap(container => container.actions)).concat(target.otherActions) + let choices = this.actions.concat( + this.containers.flatMap(container => container.actions)).concat( + target.otherActions.concat( + this.otherContainers.flatMap(container => container.actions) + ) + ) if (this.containedIn !== null) { choices = choices.concat(this.containedIn.actions) diff --git a/src/game/language.ts b/src/game/language.ts index 3f8ead1..4c21351 100644 --- a/src/game/language.ts +++ b/src/game/language.ts @@ -130,6 +130,7 @@ export class DynText { } toString (): string { + console.log(this.parts) return (this.parts.map(part => part.toString())).join('') } } diff --git a/src/game/vore.ts b/src/game/vore.ts index 9f1c7b7..99069d4 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -1,7 +1,7 @@ import { Entity, Mortal, POV, Creature } from './entity' import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats } from './combat' import { LogLines, LogEntry, CompositeLog, LogLine } from './interface' -import { Noun, Pronoun, POVPair, POVPairArgs, ImproperNoun, POVSolo } from './language' +import { Noun, Pronoun, POVPair, POVPairArgs, ImproperNoun, POVSolo, TextLike, Verb, Word } from './language' import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions' export enum VoreType { @@ -29,7 +29,8 @@ export abstract class Vore implements Mortal { abstract voreStats: VoreStats; abstract containedIn: Container | null; abstract predPrefs: Set; - abstract containers: Array; + abstract containers: Array; + abstract otherContainers: Array; destroy (): LogEntry { const lines = new POVSolo([ [[POV.First], (target: Vore) => new LogLine('You die!')], @@ -53,66 +54,101 @@ export interface Container extends Actionable { owner: Vore; voreTypes: Set; contents: Array; - digested: Array; capacity: number; fullness: number; canTake: (prey: Vore) => boolean; consume: (prey: Vore) => LogEntry; release: (prey: Vore) => LogEntry; struggle: (prey: Vore) => LogEntry; - tick: (dt: number) => LogEntry; describe: () => LogEntry; - digest: (prey: Vore) => LogEntry; - absorb: (prey: Vore) => LogEntry; - dispose: (preys: Vore[]) => LogEntry; - actions: Array; + + consumeVerb: Verb; + releaseVerb: Verb; + struggleVerb: Verb; } -abstract class NormalContainer implements Container { - contents: Array - digested: Array +export abstract class NormalContainer implements Container { + contents: Array = [] + actions: Array = [] - abstract consumeLines: POVPair - abstract releaseLines: POVPair - abstract struggleLines: POVPair - abstract tickLines: POVPairArgs - abstract digestLines: POVPair - abstract absorbLines: POVPair - abstract disposeLines: POVPair + abstract consumeLines: POVPair + abstract releaseLines: POVPair + abstract struggleLines: POVPair - get fullness (): number { - return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) - } + abstract consumeVerb: Verb + abstract releaseVerb: Verb + abstract struggleVerb: Verb - canTake (prey: Vore): boolean { - const fits = this.capacity - this.fullness >= prey.voreStats.Bulk + get fullness (): number { + return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) + } - const permitted = Array.from(this.voreTypes).every(voreType => { - return prey.preyPrefs.has(voreType) - }) + canTake (prey: Vore): boolean { + const fits = this.capacity - this.fullness >= prey.voreStats.Bulk - return fits && permitted - } + const permitted = Array.from(this.voreTypes).every(voreType => { + return prey.preyPrefs.has(voreType) + }) - consume (prey: Vore): LogEntry { - this.contents.push(prey) - prey.containedIn = this - return this.consumeLines.run(this.owner, prey) - } + return fits && permitted + } - release (prey: Vore): LogEntry { - prey.containedIn = this.owner.containedIn - this.contents = this.contents.filter(victim => victim !== prey) + consume (prey: Vore): LogEntry { + this.contents.push(prey) + prey.containedIn = this + return this.consumeLines.run(this.owner, prey) + } - if (this.owner.containedIn !== null) { - this.owner.containedIn.contents.push(prey) - } - return this.releaseLines.run(this.owner, prey) - } + release (prey: Vore): LogEntry { + prey.containedIn = this.owner.containedIn + this.contents = this.contents.filter(victim => victim !== prey) - struggle (prey: Vore): LogEntry { - return this.struggleLines.run(prey, this.owner) + if (this.owner.containedIn !== null) { + this.owner.containedIn.contents.push(prey) } + return this.releaseLines.run(this.owner, prey) + } + + struggle (prey: Vore): LogEntry { + return this.struggleLines.run(prey, this.owner) + } + + describe (): LogEntry { + const lines: Array = [] + + this.contents.forEach(prey => { + lines.push(prey.toString()) + }) + + return new LogLine(...lines) + } + + constructor (public name: Noun, public owner: Vore, public voreTypes: Set, public capacity: number) { + this.actions.push(new DevourAction(this)) + this.actions.push(new ReleaseAction(this)) + this.actions.push(new StruggleAction(this)) + } +} + +export interface VoreContainer extends Container { + digested: Array; + tick: (dt: number) => LogEntry; + digest: (prey: Vore) => LogEntry; + absorb: (prey: Vore) => LogEntry; + dispose: (preys: Vore[]) => LogEntry; +} + +export abstract class NormalVoreContainer extends NormalContainer implements VoreContainer { + digested: Array = [] + + abstract tickLines: POVPairArgs + abstract digestLines: POVPair + abstract absorbLines: POVPair + abstract disposeLines: POVPair + + consumeVerb = new Verb('devour') + releaseVerb = new Verb('release', 'releases', 'releasing', 'released') + struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled') tick (dt: number): LogEntry { const justDigested: Array = [] @@ -140,16 +176,6 @@ abstract class NormalContainer implements Container { return new LogLines(tickedEntries, new LogLines(...damageResults), digestedEntries) } - describe (): LogEntry { - const lines: Array = [] - - this.contents.forEach(prey => { - lines.push(prey.toString()) - }) - - return new LogLine(...lines) - } - digest (prey: Vore): LogEntry { return this.digestLines.run(this.owner, prey) } @@ -162,31 +188,23 @@ abstract class NormalContainer implements Container { return new CompositeLog(...preys.map(prey => this.disposeLines.run(this.owner, prey))) } - actions: Array - - constructor (public name: Noun, public owner: Vore, public voreTypes: Set, public capacity: number, private damage: Damage) { - this.contents = [] - this.digested = [] - - this.actions = [] + constructor (name: Noun, owner: Vore, voreTypes: Set, capacity: number, private damage: Damage) { + super(name, owner, voreTypes, capacity) this.name = name - this.actions.push(new DevourAction(this)) this.actions.push(new DigestAction(this)) - this.actions.push(new ReleaseAction(this)) - this.actions.push(new StruggleAction(this)) } } -abstract class InnerContainer extends NormalContainer { +abstract class InnerContainer extends NormalVoreContainer { release (prey: Vore): LogEntry { prey.containedIn = this.escape this.contents = this.contents.filter(victim => victim !== prey) return this.releaseLines.run(this.owner, prey) } - constructor (name: Noun, owner: Vore, voreTypes: Set, capacity: number, damage: Damage, private escape: Container) { + constructor (name: Noun, owner: Vore, voreTypes: Set, capacity: number, damage: Damage, private escape: VoreContainer) { super(name, owner, voreTypes, capacity, damage) this.actions = [] @@ -196,7 +214,7 @@ abstract class InnerContainer extends NormalContainer { } } -export class Stomach extends NormalContainer { +export class Stomach extends NormalVoreContainer { constructor (owner: Vore, capacity: number, damage: Damage) { super(new ImproperNoun('stomach', 'stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage) } @@ -245,7 +263,7 @@ export class Stomach extends NormalContainer { } export class InnerStomach extends InnerContainer { - constructor (owner: Vore, capacity: number, damage: Damage, escape: Container) { + constructor (owner: Vore, capacity: number, damage: Damage, escape: VoreContainer) { super(new ImproperNoun('inner stomach', 'inner stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage, escape) } @@ -292,7 +310,7 @@ export class InnerStomach extends InnerContainer { ]) } -export class Bowels extends NormalContainer { +export class Bowels extends NormalVoreContainer { constructor (owner: Vore, capacity: number, damage: Damage) { super(new ImproperNoun('bowel', 'bowels').plural, owner, new Set([VoreType.Anal]), capacity, damage) } diff --git a/src/game/words.ts b/src/game/words.ts index d34bf71..6b1d760 100644 --- a/src/game/words.ts +++ b/src/game/words.ts @@ -30,3 +30,15 @@ export const Digests = new RandomWord([ new Verb("melt"), new Verb("dissolve", "dissolves", "dissolving", "dissolved") ]) + +export const Struggles = new RandomWord([ + new Verb("squirm"), + new Verb("struggle", "struggles", "struggling", "struggled"), + new Verb("thrash", "thrashes") +]) + +export const Bulge = new RandomWord([ + new ImproperNoun("bulge", "bulges"), + new ImproperNoun("outline", "outlines"), + new ImproperNoun("imprint", "imprints") +])