|  | 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<DamageType, number>;
  abstract takeDamage (damage: Damage): LogEntry;
  abstract stats: Stats;
  abstract baseStats: Stats;
  abstract status: string;
  digested = false;
  abstract preyPrefs: Set<VoreType>;
  abstract voreStats: VoreStats;
  abstract containedIn: Container | null;
  abstract predPrefs: Set<VoreType>;
  abstract containers: Array<VoreContainer>;
  abstract otherContainers: Array<Container>;
  destroy (): LogEntry {
    const lines = new POVSolo<Vore>([
      [[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<VoreType>;
  contents: Array<Vore>;
  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<Vore> = []
  actions: Array<Action> = []
  abstract consumeLines: POVPair<Vore, Vore>
  abstract releaseLines: POVPair<Vore, Vore>
  abstract struggleLines: POVPair<Vore, Vore>
  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<string> = []
    this.contents.forEach(prey => {
      lines.push(prey.toString())
    })
    return new LogLine(...lines)
  }
  constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, 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<Vore>;
  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<Vore> = []
    abstract tickLines: POVPairArgs<Vore, Vore, { damage: Damage }>
    abstract digestLines: POVPair<Vore, Vore>
    abstract absorbLines: POVPair<Vore, Vore>
    abstract disposeLines: POVPair<Vore, Vore>
    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<Vore> = []
      const scaled = this.damage.scale(dt / 60)
      const damageResults: Array<LogEntry> = []
      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<VoreType>, 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<VoreType>, 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<Vore, Vore>([
    [[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<Vore, Vore>([
    [[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<Vore, Vore>([
    [[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<Vore, Vore, { damage: Damage }>([
    [[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<Vore, Vore>([
    [[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<Vore, Vore>([
    [[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<Vore, Vore>([
    [[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<Vore, Vore, { damage: Damage }>([
    [[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<Vore, Vore, { damage: Damage }>([
    [[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}`)]
  ])
}
 |