|  | import { Creature } from "../creature"
import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction, CombatTest, Stat, DamageFormula, UniformRandomDamageFormula, Action, DamageInstance, StatDamageFormula, VoreStat } from '../combat'
import { ImproperNoun, ProperNoun, FemalePronouns, RandomWord, Adjective, Verb, PairLine } from '../language'
import { LogLine, LogLines, LogEntry, Newline } from '../interface'
import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container } from '../vore'
import { AttackAction, FeedAction, TransferAction } from '../combat/actions'
import { TogetherCondition, ContainsCondition, EnemyCondition, AllyCondition, PairCondition, CapableCondition } from '../combat/conditions'
import { InstantKillEffect, DamageTypeResistanceEffect } from '../combat/effects'
import * as Words from '../words'
import { StatVigorTest } from '../combat/tests'
class LevelDrain extends Action {
  constructor (private container: Container) {
    super(
      'Level Drain',
      'Drain energy from your prey',
      [
        new ContainsCondition(container),
        new CapableCondition()
      ]
    )
  }
  execute (user: Creature, target: Creature): LogEntry {
    const damage: Damage = new Damage(...Object.keys(Stat).map(stat => {
      return {
        type: DamageType.Acid,
        target: stat as Stat,
        amount: target.baseStats[stat as Stat] / 5
      }
    }))
    const heal: Damage = new Damage(...Object.keys(Stat).map(stat => {
      return {
        type: DamageType.Heal,
        target: stat as Stat,
        amount: target.baseStats[stat as Stat] / 5
      }
    }))
    // TODO make this respect resistances
    user.takeDamage(heal)
    const targetResult = target.takeDamage(damage)
    return new LogLines(
      new LogLine(`${user.name.capital.possessive} ${this.container.name} drains power from ${target.name.objective}, siphoning `, damage.renderShort(), ` from ${target.pronouns.possessive} body!`),
      targetResult
    )
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Drain energy from ${target.name}`)
  }
}
class HypnotizeAction extends Action {
  constructor () {
    super(
      `Hypnotize`,
      `Change their mind!`,
      [
        new TogetherCondition(),
        new EnemyCondition(),
        new CapableCondition()
      ]
    )
  }
  line: PairLine<Creature> = (user, target) => new LogLine(
    `${user.name.capital.possessive} hypnotic gaze enthralls ${target.name}, putting ${target.pronouns.objective} under ${user.pronouns.possessive} control!`
  )
  execute (user: Creature, target: Creature): LogEntry {
    target.side = user.side
    return this.line(user, target)
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Force your target to fight by your side`)
  }
}
class MawContainer extends NormalContainer {
  consumeVerb = new Verb('grab', 'grabs', 'grabbing', 'grabbed')
  releaseVerb = new Verb('release')
  struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
  constructor (owner: Vore, stomach: VoreContainer) {
    super(new ImproperNoun('maw'), owner, new Set([VoreType.Oral]), 50)
    const transfer = new TransferAction(this, stomach)
    transfer.verb = new Verb('gulp')
    this.actions.push(transfer)
  }
}
class FlexToesAction extends GroupAction {
  constructor (private damage: DamageFormula, container: Container) {
    super('Flex Toes', 'Flex your toes!', [
      new ContainsCondition(container),
      new PairCondition()
    ])
  }
line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital.possessive} toes crush ${target.name.objective} for `, args.damage.renderShort(), ` damage!`)
describeGroup (user: Creature, targets: Creature[]): LogEntry {
  return new LogLine(`Flex your toes. `, this.damage.explain(user))
}
execute (user: Creature, target: Creature): LogEntry {
  const damage = this.damage.calc(user, target)
  return new LogLines(target.takeDamage(damage), this.line(user, target, { damage: damage }))
}
describe (user: Creature, target: Creature): LogEntry {
  return new LogLine(`Flex your toes! `, this.damage.describe(user, target))
}
}
class BootContainer extends NormalContainer {
  consumeVerb = new Verb('trap', 'traps', 'trapped', 'trapping')
  releaseVerb = new Verb('dump')
  struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
  constructor (owner: Vore) {
    super(new ImproperNoun('boot'), owner, new Set(), 50)
    const flex = new FlexToesAction(
      new UniformRandomDamageFormula(new Damage(
        { target: Stat.Toughness, type: DamageType.Crush, amount: 10 },
        { target: Stat.Power, type: DamageType.Crush, amount: 10 },
        { target: Stat.Speed, type: DamageType.Crush, amount: 10 },
        { target: Stat.Willpower, type: DamageType.Crush, amount: 30 },
        { target: Stat.Charm, type: DamageType.Crush, amount: 10 }
      ), 0.5),
      this
    )
    this.actions.push(flex)
  }
}
const huge = new RandomWord([
  new Adjective('massive'),
  new Adjective('colossal'),
  new Adjective('big ol\''),
  new Adjective('heavy'),
  new Adjective('crushing'),
  new Adjective('huge')
])
class BiteAction extends AttackAction {
  constructor () {
    super(
      new ConstantDamageFormula(new Damage({ amount: 50, type: DamageType.Slash, target: Vigor.Health })),
      new Verb('bite', 'bites', 'biting', 'bit')
    )
    this.name = "Bite"
  }
}
class ChewAction extends GroupAction {
  constructor (private damage: DamageFormula, container: Container, private killAction: Action) {
    super('Chew', 'Give them the big chew', [
      new ContainsCondition(container)
    ])
  }
  line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital} chews on ${target.name.objective} for `, args.damage.renderShort(), `!`)
  describeGroup (user: Creature, targets: Creature[]): LogEntry {
    return new LogLine('Crunch \'em all. ', this.damage.explain(user))
  }
  execute (user: Creature, target: Creature): LogEntry {
    const damage = this.damage.calc(user, target)
    const results: Array<LogEntry> = []
    results.push(this.line(user, target, { damage: damage }))
    results.push(new LogLine(' '))
    results.push(target.takeDamage(damage))
    if (target.vigors.Health <= 0) {
      if (this.killAction.allowed(user, target)) {
        results.push(new Newline())
        results.push(this.killAction.execute(user, target))
      }
    }
    return new LogLine(...results)
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine('Do the crunch')
  }
}
class StompAction extends GroupAction {
  constructor () {
    super('Stomp', 'STOMP!', [
      new TogetherCondition(),
      new EnemyCondition(),
      new CapableCondition()
    ])
  }
  line: PairLine<Creature> = (user, target) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(new Verb('flatten'))} ${target.name.objective} under ${user.pronouns.possessive} ${huge} foot!`
  )
  execute (user: Creature, target: Creature): LogEntry {
    return new LogLines(this.line(user, target), target.applyEffect(new InstantKillEffect()))
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine('Stomp one sucker')
  }
  describeGroup (user: Creature, targets: Array<Creature>): LogEntry {
    return new LogLine('Stomp all ', targets.length.toString(), ' of \'em!')
  }
}
class StompAllyAction extends Action {
  constructor () {
    super('Stomp Ally', '-1 ally, +1 buff', [
      new TogetherCondition(),
      new AllyCondition(),
      new CapableCondition()
    ])
  }
  line: PairLine<Creature> = (user, target) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(new Verb('flatten'))} ${target.name.objective} under ${user.pronouns.possessive} ${huge} boot!`
  )
  execute (user: Creature, target: Creature): LogEntry {
    const damages: Array<DamageInstance> = Object.keys(Stat).map(stat => ({
      target: stat as Stat,
      amount: target.stats[stat as Stat] / 3,
      type: DamageType.Heal
    }))
    const heal = new Damage(
      ...damages
    )
    user.takeDamage(heal)
    target.destroyed = true
    return new LogLines(
      this.line(user, target),
      target.applyEffect(new InstantKillEffect()),
      user.applyEffect(new DamageTypeResistanceEffect(
        [DamageType.Crush, DamageType.Slash, DamageType.Pierce],
        0.5
      )),
      new LogLine(`${user.name.capital} absorbs ${target.pronouns.possessive} power, gaining `, heal.renderShort())
    )
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine('Crush an ally to absorb their power')
  }
}
class DevourAllAction extends GroupAction {
  private test: CombatTest
  constructor (private container: VoreContainer) {
    super('Devour All', 'GULP!', [
      new TogetherCondition(),
      new EnemyCondition(),
      new CapableCondition()
    ])
    this.test = new StatVigorTest(Stat.Power)
  }
  line = (user: Creature, target: Creature) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('scoop'))} ${target.name} up!`)
  groupLine = (user: Creature, args: { count: number }) => new LogLine(`${Words.SwallowSound.allCaps}! All ${args.count} of ${user.pronouns.possessive} prey pour down ${user.name.possessive} ${Words.Slick} gullet as ${user.pronouns.subjective} ${user.name.conjugate(Words.Swallows)}; they're just ${user.kind.all} chow now`)
  execute (user: Creature, target: Creature): LogEntry {
    this.container.consume(target)
    return new LogLines(this.line(user, target))
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine('Stomp one sucker')
  }
  executeGroup (user: Creature, targets: Array<Creature>): LogEntry {
    return new LogLines(...targets.filter(target => this.test.test(user, target)).map(target => this.execute(user, target)).concat(
      [
        new Newline(),
        this.groupLine(user, { count: targets.length })
      ]
    ))
  }
  describeGroup (user: Creature, targets: Array<Creature>): LogEntry {
    return new LogLine('Eat all ', targets.length.toString(), ' of \'em!')
  }
}
export class Withers extends Creature {
  title = "Huge Hellhound"
  desc = "Will eat your party"
  constructor () {
    super(
      new ProperNoun('Withers'),
      new ImproperNoun('hellhound', 'hellhounds'),
      FemalePronouns,
      { Toughness: 40, Power: 10, Speed: 30, Willpower: 40, Charm: 70 },
      new Set(),
      new Set([VoreType.Oral]),
      5000
    )
    this.actions.push(new BiteAction())
    this.groupActions.push(new StompAction())
    this.side = Side.Monsters
    const stomach = new Stomach(this, 50, new Damage(
      { amount: 300, type: DamageType.Acid, target: Vigor.Health },
      { amount: 200, type: DamageType.Crush, target: Vigor.Stamina },
      { amount: 200, type: DamageType.Dominance, target: Vigor.Resolve }
    ))
    this.containers.push(stomach)
    this.otherActions.push(new FeedAction(stomach))
    this.groupActions.push(new DevourAllAction(stomach))
    const maw = new MawContainer(this, stomach)
    this.otherContainers.push(maw)
    const transfer = new TransferAction(maw, stomach)
    transfer.verb = new Verb('gulp')
    this.actions.push(new ChewAction(
      new UniformRandomDamageFormula(new Damage(
        { target: Vigor.Health, type: DamageType.Crush, amount: 10000 }
      ), 0.5),
      maw,
      transfer
    ))
    const boot = new BootContainer(this)
    this.otherContainers.push(boot)
    this.actions.push(new StompAllyAction())
    this.actions.push(
      new AttackAction(
        new StatDamageFormula([
          { fraction: 0.5, stat: Stat.Toughness, target: Vigor.Health, type: DamageType.Crush },
          { fraction: 0.05, stat: VoreStat.Bulk, target: Vigor.Health, type: DamageType.Crush }
        ]),
        new Verb('stomp')
      )
    )
    this.actions.push(new HypnotizeAction())
    this.actions.push(new LevelDrain(stomach))
  }
}
 |