|  | import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat } from './combat'
import { Noun, Pronoun } from './language'
import { LogEntry, LogLines } from './interface'
import { Vore, VoreContainer, VoreType } from './vore'
import { Item, EquipmentSlot, Equipment, ItemKind } from './items'
import { PassAction } from './combat/actions'
import { AI, NoAI } from './ai'
export class Creature extends Vore implements Combatant {
  actions: Array<Action> = [];
  containedIn: VoreContainer | null = null;
  desc = "Some creature";
  get effects (): Array<Effective> {
    return (this.statusEffects as Effective[]).concat(
      Object.values(this.equipment).filter(item => item !== undefined).flatMap(
        item => (item as Equipment).effects
      )
    )
  }
  statusEffects: Array<StatusEffect> = [];
  groupActions: Array<GroupAction> = [];
  items: Array<Item> = [];
  otherActions: Array<Action> = [];
  side: Side;
  title = "Lv. 1 Creature";
  equipment: {[key in EquipmentSlot]?: Equipment } = {}
  ai: AI = new NoAI()
  constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, preyPrefs: Set<VoreType>, predPrefs: Set<VoreType>, mass: number) {
    super(name, kind, pronouns, stats, preyPrefs, predPrefs, mass)
    this.actions.push(new PassAction())
    this.side = Side.Heroes
    const baseVoreStats = this.voreStats
    /* eslint-disable-next-line */
    const self = this
    this.voreStats = {
      get [VoreStat.Bulk] () {
        return baseVoreStats.Bulk
      },
      get [VoreStat.Mass] () {
        const base = baseVoreStats.Mass
        const adjusted = self.effects.reduce((scale: number, effect: Effective) => effect.scale(scale), base)
        return adjusted
      },
      get [VoreStat.PreyCount] () {
        return baseVoreStats["Prey Count"]
      }
    }
  }
  applyEffect (effect: StatusEffect): LogEntry {
    this.statusEffects.push(effect)
    return effect.onApply(this)
  }
  /**
   * Determines how much damage an attack would do
   */
  effectiveDamage (damage: Damage): Damage {
    const preDamage = this.effects.reduce((modifiedDamage: Damage, effect: Effective) => {
      return effect.preDamage(this, modifiedDamage)
    }, damage)
    return super.effectiveDamage(preDamage)
  }
  resistanceTo (damageType: DamageType) {
    const base = super.resistanceTo(damageType)
    const modified = this.effects.reduce((resist, effect) => effect.modResistance(damageType, resist), base)
    return modified
  }
  executeAction (action: Action, target: Creature): LogEntry {
    const preActionResults = this.effects.map(effect => effect.preAction(this))
    const preReceiveActionResults = target.effects.map(effect => effect.preReceiveAction(target, this))
    const blocking = preActionResults.concat(preReceiveActionResults).filter(result => result.prevented)
    if (blocking.length > 0) {
      return new LogLines(...blocking.map(result => result.log))
    } else {
      return action.execute(this, target)
    }
  }
  removeEffect (effect: StatusEffect): LogEntry {
    this.statusEffects = this.statusEffects.filter(eff => eff !== effect)
    return effect.onRemove(this)
  }
  equip (item: Equipment, slot: EquipmentSlot) {
    const equipped = this.equipment[slot]
    if (equipped !== undefined) {
      this.unequip(slot)
    }
    this.equipment[slot] = item
  }
  unequip (slot: EquipmentSlot) {
    const item = this.equipment[slot]
    if (item !== undefined) {
      this.items.push(item)
      this.equipment[slot] = undefined
    }
  }
  get status (): Array<VisibleStatus> {
    const results: Array<VisibleStatus> = []
    if (this.vigors[Vigor.Health] <= 0) {
      results.push(new ImplicitStatus('Dead', 'Out of health', 'fas fa-heart'))
    }
    if (this.vigors[Vigor.Stamina] <= 0) {
      results.push(new ImplicitStatus('Unconscious', 'Out of stamina', 'fas fa-bolt'))
    }
    if (this.vigors[Vigor.Resolve] <= 0) {
      results.push(new ImplicitStatus('Broken', 'Out of resolve', 'fas fa-brain'))
    }
    if (this.containedIn !== null) {
      results.push(new ImplicitStatus('Eaten', 'Devoured by ' + this.containedIn.owner.name, 'fas fa-drumstick-bite'))
    }
    this.statusEffects.forEach(effect => {
      results.push(effect)
    })
    return results
  }
  validActions (target: Creature): Array<Action> {
    let choices = ([] as Action[]).concat(
      this.actions,
      this.containers.flatMap(container => container.actions),
      target.otherActions,
      this.otherContainers.flatMap(container => container.actions),
      Object.values(this.equipment).filter(item => item !== undefined).flatMap(item => (item as Equipment).actions),
      this.items.filter(item => item.kind === ItemKind.Consumable && !item.consumed).flatMap(item => item.actions)
    )
    if (this.containedIn !== null) {
      choices = choices.concat(this.containedIn.actions)
    }
    return choices.filter(action => {
      return action.allowed(this, target)
    })
  }
  validGroupActions (targets: Array<Creature>): Array<GroupAction> {
    const choices = this.groupActions
    return choices.filter(action => {
      return targets.some(target => action.allowed(this, target))
    })
  }
}
 |