import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective } 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 = []; containedIn: VoreContainer | null = null; desc = "Some creature"; get effects (): Array { return (this.statusEffects as Effective[]).concat( Object.values(this.equipment).filter(item => item !== undefined).flatMap( item => (item as Equipment).effects ) ) } statusEffects: Array = []; groupActions: Array = []; items: Array = []; otherActions: Array = []; 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, predPrefs: Set, mass: number) { super(name, kind, pronouns, stats, preyPrefs, predPrefs, mass) this.actions.push(new PassAction()) this.side = Side.Heroes } applyEffect (effect: StatusEffect): LogEntry { this.effects.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.items.push(equipped) } this.equipment[slot] = item } get status (): Array { const results: Array = [] 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 { 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): Array { const choices = this.groupActions return choices.filter(action => { return targets.some(target => action.allowed(this, target)) }) } }