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, TextLike, Verb, Word } from './language' import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions' import * as Words from './words' export enum VoreType { Oral = "Oral Vore", Anal = "Anal Vore", Cock = "Cock Vore", Unbirth = "Unbirthing" } export abstract class Vore implements Mortal { abstract name: Noun; abstract title: TextLike; abstract desc: TextLike; abstract kind: Noun; abstract pronouns: Pronoun; abstract perspective: POV; abstract vigors: {[key in Vigor]: number}; abstract maxVigors: {[key in Vigor]: number}; abstract disabled: boolean; abstract resistances: Map; abstract takeDamage (damage: Damage): LogEntry; abstract stats: Stats; abstract baseStats: Stats; abstract status: string; digested = false; abstract preyPrefs: Set; abstract voreStats: VoreStats; abstract containedIn: Container | null; abstract predPrefs: Set; abstract containers: Array; abstract otherContainers: Array; destroy (): LogEntry { const lines = new POVSolo([ [[POV.First], (target: Vore) => new LogLine('You die!')], [[POV.Third], (target: Vore) => new LogLine(`${target.name.capital} dies!`)] ]) this.containers.map(container => { container.contents.map(prey => { prey.containedIn = this.containedIn if (this.containedIn !== null) { this.containedIn.contents.push(prey) } }) }) return lines.run(this) } } export interface Container extends Actionable { name: Noun; owner: Vore; voreTypes: Set; contents: Array; capacity: number; fullness: number; canTake: (prey: Vore) => boolean; consume: (prey: Vore) => LogEntry; release: (prey: Vore) => LogEntry; struggle: (prey: Vore) => LogEntry; describe: () => LogEntry; consumeVerb: Verb; releaseVerb: Verb; struggleVerb: Verb; } export abstract class NormalContainer implements Container { public name: Noun contents: Array = [] actions: Array = [] abstract consumeLines: POVPair abstract releaseLines: POVPair abstract struggleLines: POVPair abstract consumeVerb: Verb abstract releaseVerb: Verb abstract struggleVerb: Verb get fullness (): number { return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) } canTake (prey: Vore): boolean { const fits = this.capacity - this.fullness >= prey.voreStats.Bulk const permitted = Array.from(this.voreTypes).every(voreType => { return prey.preyPrefs.has(voreType) }) return fits && permitted } consume (prey: Vore): LogEntry { this.contents.push(prey) prey.containedIn = this return this.consumeLines.run(this.owner, prey) } release (prey: Vore): LogEntry { prey.containedIn = this.owner.containedIn this.contents = this.contents.filter(victim => victim !== prey) 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 (name: Noun, public owner: Vore, public voreTypes: Set, public capacity: number) { this.name = name.all 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 = [] const scaled = this.damage.scale(dt / 60) const damageResults: Array = [] this.contents.forEach(prey => { damageResults.push(prey.takeDamage(scaled)) if (prey.vigors[Vigor.Health] <= 0) { prey.digested = true this.digested.push(prey) justDigested.push(prey) } }) const tickedEntries = new LogLines(...this.contents.map(prey => this.tickLines.run(this.owner, prey, { damage: scaled }))) const digestedEntries = new LogLines(...justDigested.map(prey => this.digest(prey))) this.contents = this.contents.filter(prey => { return prey.vigors[Vigor.Health] > 0 }) return new LogLines(tickedEntries, new LogLines(...damageResults), digestedEntries) } digest (prey: Vore): LogEntry { return this.digestLines.run(this.owner, prey) } absorb (prey: Vore): LogEntry { return this.absorbLines.run(this.owner, prey) } dispose (preys: Vore[]): LogEntry { return new CompositeLog(...preys.map(prey => this.disposeLines.run(this.owner, prey))) } constructor (name: Noun, owner: Vore, voreTypes: Set, capacity: number, private damage: Damage) { super(name, owner, voreTypes, capacity) this.name = name this.actions.push(new DigestAction(this)) } } 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: VoreContainer) { super(name, owner, voreTypes, capacity, damage) this.actions = [] this.actions.push(new DigestAction(this)) this.actions.push(new StruggleAction(this)) } } 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) } consumeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name}`)] ]) releaseLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name}`)] ]) struggleLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)] ]) tickLines = new POVPairArgs([ [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach ${Words.Churns.singular} ${target.name} for `, args.damage.renderShort())], [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach ${Words.Churns.singular} you for `, args.damage.renderShort())], [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} ${Words.Churns.singular} ${target.name} for `, args.damage.renderShort())] ]) 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 ${Words.Struggles.present} fades as the ${target.kind.all} is ${Words.Digests.past} by the ${user.kind.all}'s ${this.name}.`)] ]) absorbLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) } export class InnerStomach extends InnerContainer { 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) } consumeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name.capital}`)] ]) releaseLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name.capital}`)] ]) struggleLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)] ]) tickLines = new POVPairArgs([ [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach gurgles ${target.name} for `, args.damage.renderShort())], [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach churns you for `, args.damage.renderShort())], [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} churns ${target.name} for `, args.damage.renderShort())] ]) digestLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)] ]) absorbLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) } 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) } consumeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You force ${target.name} into your bowels`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} works you into ${user.pronouns.possessive} ass`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} anal-vores ${target.name.capital}`)] ]) 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.capital}`)] ]) struggleLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way out your rump!`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the bowels of ${target.name}`)] ]) tickLines = new POVPairArgs([ [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your bowels gurgle ${target.name} for `, args.damage.renderShort())], [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s bowels churn you for `, args.damage.renderShort())], [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${target.name.capital} churns ${user.name} for `, args.damage.renderShort())] ]) digestLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your bowels overwhelm ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s bowels finish you off`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the bowels of ${user.name}`)] ]) absorbLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) }