|  | import { TestCategory, CompositionTest, OpposedStatScorer } from '@/game/combat/tests'
import { DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '@/game/language'
import { Entity } from '@/game/entity'
import { Creature } from "../creature"
import { Damage, DamageFormula, Vigor, Action, Condition, CombatTest, CompositionAction } from '@/game/combat'
import { LogLine, LogLines, LogEntry } from '@/game/interface'
import { Connection, Container } from '@/game/vore'
import { CapableCondition, UserDrainedVigorCondition, TogetherCondition, EnemyCondition, SoloCondition, PairCondition, ContainsCondition, ContainedByCondition, HasRoomCondition } from '@/game/combat/conditions'
import { ConsumeConsequence } from '@/game/combat/consequences'
/**
 * The PassAction has no effect.
 */
export class PassAction extends Action {
  constructor () {
    super("Pass", "Do nothing", [new SoloCondition()])
  }
  execute (user: Creature, target: Creature): LogEntry {
    return new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('pass', 'passes'))}`)
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine("Do nothing.")
  }
}
/**
 * A generic action that causes damage.
 */
export abstract class DamageAction extends Action {
  abstract successLine: PairLineArgs<Creature, { damage: Damage }>
  constructor (name: TextLike, desc: TextLike, protected damage: DamageFormula, tests: CombatTest[], conditions: Condition[] = []) {
    super(
      name,
      desc,
      [new CapableCondition(), new EnemyCondition()].concat(conditions),
      tests
    )
  }
  // TODO: remove me or replace the logic for damage prevention
  execute (user: Creature, target: Creature): LogEntry {
    const damage = this.damage.calc(user, target)
    const targetResult = target.takeDamage(damage)
    const ownResult = this.successLine(user, target, { damage: damage })
    return new LogLines(ownResult, targetResult)
  }
}
/**
 * Adds flavor text to [[DamageAction]]
 */
export class AttackAction extends DamageAction {
  constructor (damage: DamageFormula, protected verb: Verb = new Verb('smack')) {
    super(
      verb.root.capital,
      'Attack the enemy',
      damage,
      [
        new CompositionTest(
          [
            new OpposedStatScorer(
              {
                Power: 1
              },
              {
                Reflexes: 1
              }
            )
          ],
          (user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb("swing"))} and ${user.name.conjugate(new Verb("miss", "misses"))} ${target.name.objective}.`),
          TestCategory.Attack,
          0
        )
      ],
      [new TogetherCondition()]
    )
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Attack ${target.baseName}. `, this.damage.describe(user, target), super.describe(user, target))
  }
  successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `,
    target.effectiveDamage(args.damage).renderShort()
  )
}
/**
 * Devours the target.
 */
export class DevourAction extends CompositionAction {
  constructor (protected container: Container) {
    super(
      new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'),
      new LiveText(container, x => `Try to ${x.consumeVerb} your foe`),
      {
        conditions: [new CapableCondition(), new TogetherCondition(), new HasRoomCondition(container)],
        tests: [
          new CompositionTest(
            [
              new OpposedStatScorer(
                { Power: 1, Charm: 1, Mass: 0.05 },
                { Toughness: 1, Willpower: 1, Bulk: 0.05 }
              )
            ],
            (user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${container.consumeVerb} ${target.name.objective}.`),
            TestCategory.Vore,
            -5
          )],
        consequences: [
          new ConsumeConsequence(container)
        ]
      }
    )
  }
  allowed (user: Creature, target: Creature): boolean {
    const owner = this.container.owner === user
    const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref))
    const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref))
    if (owner && predOk && preyOk) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Try to ${this.container.consumeVerb} your opponent, sending them to your ${this.container.name}. `, super.describe(user, target))
  }
}
/**
 * Causes the user to be eaten by the target.
 */
export class FeedAction extends Action {
  constructor (protected container: Container) {
    super(
      'Feed',
      'Feed yourself to your opponent',
      [new UserDrainedVigorCondition(Vigor.Resolve), new TogetherCondition()]
    )
    this.name += ` (${container.name})`
  }
  allowed (user: Creature, target: Creature): boolean {
    const owner = this.container.owner === target
    const predOk = Array.from(this.container.voreTypes).every(pref => user.preyPrefs.has(pref))
    const preyOk = Array.from(this.container.voreTypes).every(pref => target.predPrefs.has(pref))
    if (owner && predOk && preyOk) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  execute (user: Creature, target: Creature): LogEntry {
    return new LogLines(this.successLine(user, target), this.container.consume(user))
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name.possessive} ${this.container.name}...`)
  }
  protected successLine: PairLine<Entity> = (user, target) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(new Verb('feed'))} ${user.pronouns.reflexive} to ${target.name}. `
  )
}
/**
 * Tries to escape from the target's container
 */
export class StruggleAction extends Action {
  constructor (public container: Container) {
    super(
      new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'),
      'Try to escape from your foe',
      [new CapableCondition(), new PairCondition(), new ContainedByCondition(container)],
      [
        new CompositionTest(
          [
            new OpposedStatScorer(
              { Power: 1, Agility: 1, Bulk: 0.05 },
              { Toughness: 1, Reflexes: 1, Mass: 0.05 }
            )
          ],
          (user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to escape from ${target.name.possessive} ${container.name}.`),
          TestCategory.Vore,
          -5
        )
      ]
    )
  }
  execute (user: Creature, target: Creature): LogEntry {
    if (user.containedIn !== null) {
      return new LogLines(this.successLine(user, target, { container: this.container }), user.containedIn.release(user))
    } else {
      return new LogLine("Vore's bugged!")
    }
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Try to escape from ${target.baseName.possessive} ${this.container.name}. `, super.describe(user, target))
  }
  protected successLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine(
    `${prey.name.capital} ${prey.name.conjugate(new Verb('escape'))}!`
  )
}
/**
 * Tries to move between containers
 */
export class StruggleMoveAction extends Action {
  constructor (public from: Container, public to: Container) {
    super(
      new DynText('Struggle (', new LiveText(from, x => x.name.all), ' to ', new LiveText(to, x => x.name.all), ')'),
      'Try to escape from your foe',
      [new CapableCondition(), new PairCondition(), new ContainedByCondition(from)],
      [
        new CompositionTest(
          [
            new OpposedStatScorer(
              { Power: 1, Agility: 1, Bulk: 0.05 },
              { Toughness: 1, Reflexes: 1, Mass: 0.05 }
            )
          ],
          (user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to escape from ${target.name.possessive} ${from.name}.`),
          TestCategory.Vore,
          -5
        )
      ]
    )
  }
  execute (user: Creature, target: Creature): LogEntry {
    if (user.containedIn !== null) {
      return new LogLines(this.successLine(user, target, { container: this.from }), user.containedIn.release(user))
    } else {
      return new LogLine("Vore's bugged!")
    }
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Try to escape from ${target.baseName.possessive} ${this.from.name}. `, super.describe(user, target))
  }
  protected successLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine(
    `${prey.name.capital} ${prey.name.conjugate(new Verb('escape'))}!`
  )
}
export class RubAction extends Action {
  constructor (protected container: Container) {
    super(
      new DynText('Rub (', new LiveText(container, container => container.name.all), ')'),
      'Digest your prey more quickly',
      [new CapableCondition(), new SoloCondition()]
    )
  }
  allowed (user: Creature, target: Creature) {
    if (this.container.owner === user && this.container.contents.length > 0) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  execute (user: Creature, target: Creature): LogEntry {
    const results = this.container.tick(60)
    return new LogLines(results)
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Digest everyone inside of your ${this.container.name}.`)
  }
}
export class ReleaseAction extends Action {
  constructor (protected container: Container) {
    super(
      new DynText('Release (', new LiveText(container, x => x.name.all), ')'),
      'Release one of your prey',
      [new CapableCondition(), new PairCondition(), new ContainsCondition(container)]
    )
  }
  allowed (user: Creature, target: Creature) {
    if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  execute (user: Creature, target: Creature): LogEntry {
    return this.container.release(target)
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Release ${target.baseName} from your ${this.container.name}.`)
  }
}
export class TransferAction extends Action {
  verb: Verb = new Verb('send')
  constructor (public from: Container, public to: Connection, name = 'Transfer') {
    super(
      name,
      `${from.name.all.capital} to ${to.destination.name.all}`,
      [new CapableCondition(), new PairCondition()]
    )
  }
  line: PairLineArgs<Creature, { from: Container; to: Connection }> = (user, target, args) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.destination.name}`
  )
  allowed (user: Creature, target: Creature) {
    if (target.containedIn === this.from && this.from.contents.includes(target)) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  execute (user: Creature, target: Creature): LogEntry {
    this.from.release(target)
    this.to.destination.consume(target)
    return this.line(user, target, { from: this.from, to: this.to })
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Push ${target.baseName} from your ${this.from.name} to your ${this.to.destination.name}`)
  }
}
export class WillingTransferAction extends Action {
  giveIn: Verb = new Verb('give in', 'gives in', 'gave in', 'giving in')
  allow: Verb = new Verb('allow')
  verb: Verb = new Verb('send')
  constructor (protected from: Container, protected to: Container, name = 'Transfer') {
    super(
      name,
      `${from.name.all.capital} to ${to.name.all}`,
      [new CapableCondition(), new PairCondition()]
    )
  }
  line: PairLineArgs<Creature, { from: Container; to: Container }> = (user, target, args) => new LogLine(
    `${user.name.capital} ${user.name.conjugate(this.giveIn)} and ${user.name.conjugate(this.allow)} ${target.name.objective} to send ${user.name.objective} from ${target.pronouns.possessive} ${args.from.name} to ${target.pronouns.possessive} ${args.to.name}`
  )
  allowed (user: Creature, target: Creature) {
    if (user.containedIn === this.from && this.from.contents.includes(user)) {
      return super.allowed(user, target)
    } else {
      return false
    }
  }
  execute (user: Creature, target: Creature): LogEntry {
    this.from.release(user)
    this.to.consume(user)
    return this.line(user, target, { from: this.from, to: this.to })
  }
  describe (user: Creature, target: Creature): LogEntry {
    return new LogLine(`Allow ${target.baseName} to pull ${user.pronouns.subjective} ${this.to.consumePreposition} ${target.pronouns.possessive} ${this.to.name}`)
  }
}
 |