import { DamageType, Damage, Stats, Vigor, VoreStats, VoreStat, Stat, Vigors, DamageInstance } from './combat' import { Noun, Pronoun, TextLike, POV, PronounAsNoun, FirstPersonPronouns, SecondPersonPronouns } from './language' import { LogEntry, LogLine } from './interface' import { Place, Nowhere } from './world' export abstract class Entity { get name (): Noun { if (this.perspective === POV.First) { return new PronounAsNoun(FirstPersonPronouns) } else if (this.perspective === POV.Second) { return new PronounAsNoun(SecondPersonPronouns) } else { return this.baseName } } get pronouns (): Pronoun { if (this.perspective === POV.First) { return FirstPersonPronouns } else if (this.perspective === POV.Second) { return SecondPersonPronouns } else { return this.basePronouns } } desc: TextLike = "It's a ting." perspective: POV = POV.Third title: TextLike = "Some thing." location: Place constructor (public baseName: Noun, public kind: Noun, public basePronouns: Pronoun) { this.location = Nowhere } } export type Resistances = {[key in DamageType]: number} export abstract class Mortal extends Entity { baseResistances: Resistances stats: Stats; vigors: {[key in Vigor]: number} = { [Vigor.Health]: 100, [Vigor.Stamina]: 100, [Vigor.Resolve]: 100 } destroyed = false; constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) { super(name, kind, pronouns) this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {}) this.baseResistances = Object.keys(DamageType).reduce((resist: any, key) => { resist[key] = 1; return resist }, {}) Object.entries(this.maxVigors).forEach(([key, val]) => { this.vigors[key as Vigor] = val }) } resistanceTo (damageType: DamageType): number { return this.baseResistances[damageType] } get maxVigors (): Readonly { return { Health: this.stats.Toughness * 10 + this.stats.Power * 5, Resolve: this.stats.Willpower * 10 + this.stats.Charm * 5, Stamina: this.stats.Speed * 10 + this.stats.Power * 2.5 + this.stats.Charm * 2.5 } } get disabled (): boolean { return Object.values(this.vigors).some(val => val <= 0) } effectiveDamage (damage: Damage): Damage { const newDamages: DamageInstance[] = [] damage.damages.forEach(instance => { const factor = instance.type === DamageType.Heal ? -1 : 1 const baseResistance: number = this.resistanceTo(instance.type) const resistance = baseResistance * factor newDamages.push({ amount: instance.amount * resistance, target: instance.target, type: instance.type }) }) return new Damage(...newDamages) } takeDamage (damage: Damage): LogEntry { // first, we record health to decide if the entity just died const startHealth = this.vigors.Health damage = this.effectiveDamage(damage) damage.damages.forEach(instance => { if (instance.target in Vigor) { // just deal damage this.vigors[instance.target as Vigor] -= instance.amount } else if (instance.target in Stat) { // drain the stats, then deal damage to match const startVigors = this.maxVigors this.stats[instance.target as Stat] -= instance.amount const endVigors = this.maxVigors Object.keys(Vigor).map(vigor => { this.vigors[vigor as Vigor] -= startVigors[vigor as Vigor] - endVigors[vigor as Vigor] }) } }) Object.keys(Vigor).forEach(vigorStr => { const vigor = vigorStr as Vigor if (this.vigors[vigor] > this.maxVigors[vigor]) { this.vigors[vigor] = this.maxVigors[vigor] } }) if (this.vigors.Health <= -this.maxVigors.Health) { this.destroyed = true } if (this.vigors.Health <= 0 && startHealth > 0) { return this.destroy() } else { return new LogLine() } } toString (): string { return this.name.toString() } abstract destroy (): LogEntry; }