diff --git a/src/game/creatures/cafat.ts b/src/game/creatures/cafat.ts index c265bc9..4547ae8 100644 --- a/src/game/creatures/cafat.ts +++ b/src/game/creatures/cafat.ts @@ -15,9 +15,9 @@ class BellyCrushAction extends AttackAction { constructor (_damage: Damage) { super({ - calc (user) { return _damage.scale(user.bulk / 25) }, - describe (user) { return new LogLine('Deal ', _damage.scale(user.bulk / 25).renderShort(), ` with your ${user.bulk} `, new FAElem('fas fa-weight-hanging')) }, - explain (user) { return new LogLine('Deal ', _damage.scale(user.bulk / 25).renderShort(), ` with your ${user.bulk} `, new FAElem('fas fa-weight-hanging')) } + calc (user) { return _damage.scale(user.voreStats.Bulk / 25) }, + describe (user) { return new LogLine('Deal ', _damage.scale(user.voreStats.Bulk / 25).renderShort(), ` with your ${user.voreStats.Bulk} `, new FAElem('fas fa-weight-hanging')) }, + explain (user) { return new LogLine('Deal ', _damage.scale(user.voreStats.Bulk / 25).renderShort(), ` with your ${user.voreStats.Bulk} `, new FAElem('fas fa-weight-hanging')) } }) this.name = 'Belly Crush' this.desc = 'Use your weight!' diff --git a/src/game/entity.ts b/src/game/entity.ts index 0a359f6..f2b03a0 100644 --- a/src/game/entity.ts +++ b/src/game/entity.ts @@ -1,37 +1,47 @@ import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat, Side, GroupAction, Vigors, VisibleStatus, ImplicitStatus, StatusEffect } from './combat' -import { Noun, Pronoun, TextLike, POV } from './language' +import { Noun, Pronoun, TextLike, POV, PronounAsNoun, FirstPersonPronouns, SecondPersonPronouns } from './language' import { LogEntry, LogLine, LogLines } from './interface' import { Vore, VoreContainer, VoreType, Container } from './vore' import { Item } from './items' import { PassAction } from './combat/actions' -export interface Entity { - name: Noun; - pronouns: Pronoun; - baseName: Noun; - basePronouns: Pronoun; - title: TextLike; - desc: TextLike; - perspective: POV; +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 + } + } + + title: TextLike = "Some thing." + desc: TextLike = "It's a ting." + perspective: POV = POV.Third + + constructor (public baseName: Noun, public kind: Noun, public basePronouns: Pronoun) { + + } } -export interface Mortal extends Entity { - kind: Noun; - vigors: {[key in Vigor]: number}; - maxVigors: Readonly<{[key in Vigor]: number}>; - disabled: boolean; - resistances: Map; - takeDamage: (damage: Damage) => void; +export abstract class Mortal extends Entity { + abstract destroy (): LogEntry; + abstract effectiveDamage (damage: Damage): Damage + resistances: Map = new Map() stats: Stats; - baseStats: Stats; - status: VisibleStatus[]; - destroy: () => LogEntry; -} -export class Creature extends Vore implements Combatant { - title = "Lv. 1 Creature" - desc = "Some creature" - vigors = { + vigors: {[key in Vigor]: number} = { [Vigor.Health]: 100, [Vigor.Stamina]: 100, [Vigor.Resolve]: 100 @@ -45,111 +55,10 @@ export class Creature extends Vore implements Combatant { } } - baseStats: Stats - voreStats: VoreStats - side: Side - - effects: Array = [] - - applyEffect (effect: StatusEffect): LogEntry { - this.effects.push(effect) - return effect.onApply(this) - } - - removeEffect (effect: StatusEffect): LogEntry { - this.effects = this.effects.filter(eff => eff !== effect) - return effect.onRemove(this) - } - - executeAction (action: Action, target: Creature): LogEntry { - const effectResults = this.effects.map(effect => effect.preAction(this)) - const blocking = effectResults.filter(result => result.prevented) - if (blocking.length > 0) { - return new LogLines(...blocking.map(result => result.log)) - } else { - return action.execute(this, target) - } - } - get disabled (): boolean { return Object.values(this.vigors).some(val => val <= 0) } - resistances: Map = new Map() - perspective: POV = POV.Third - containers: Array = [] - otherContainers: Array = [] - actions: Array = [] - groupActions: Array = [] - otherActions: Array = [] - - items: Array = [] - - get bulk (): number { - return this.voreStats.Mass + this.containers.reduce((total, conatiner) => { return total + conatiner.contents.reduce((total, prey) => total + prey.voreStats.Bulk, 0) }, 0) - } - - containedIn: VoreContainer|null = null; - - constructor (public baseName: Noun, public kind: Noun, public basePronouns: Pronoun, public stats: Stats, public preyPrefs: Set, public predPrefs: Set, mass: number) { - super() - const containers = this.containers - - this.actions.push(new PassAction()) - Object.entries(this.maxVigors).forEach(([key, val]) => { - this.vigors[key as Vigor] = val - }) - this.baseStats = Object.keys(Stat).reduce((base: any, key) => { base[key] = stats[key as Stat]; return base }, {}) - this.side = Side.Heroes - this.voreStats = { - get [VoreStat.Bulk] () { - return containers.reduce( - (total: number, container: VoreContainer) => { - return total + container.contents.reduce( - (total: number, prey: Vore) => { - return total + prey.voreStats.Bulk - }, - 0 - ) + container.digested.reduce( - (total: number, prey: Vore) => { - return total + prey.voreStats.Bulk - }, - 0 - ) - }, - this.Mass - ) - }, - [VoreStat.Mass]: mass, - get [VoreStat.PreyCount] () { - return containers.reduce( - (total: number, container: VoreContainer) => { - return total + container.contents.reduce( - (total: number, prey: Vore) => { - return total + 1 + prey.voreStats[VoreStat.PreyCount] - }, - 0 - ) - }, - 0 - ) - } - } - } - - toString (): string { - return this.name.toString() - } - - /** - * Determines how much damage an attack would do - */ - effectiveDamage (damage: Damage): Damage { - return this.effects.reduce((modifiedDamage: Damage, effect: StatusEffect) => { - return effect.preDamage(this, modifiedDamage) - }, damage) - } - takeDamage (damage: Damage): LogEntry { // first, we record health to decide if the entity just died const startHealth = this.vigors.Health @@ -183,6 +92,46 @@ export class Creature extends Vore implements Combatant { } } + toString (): string { + return this.name.toString() + } + + constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) { + super(name, kind, pronouns) + Object.entries(this.maxVigors).forEach(([key, val]) => { + this.vigors[key as Vigor] = val + }) + + this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {}) + } +} + +export class Creature extends Vore implements Combatant { + title = "Lv. 1 Creature" + desc = "Some creature" + side: Side + + effects: Array = [] + + applyEffect (effect: StatusEffect): LogEntry { + this.effects.push(effect) + return effect.onApply(this) + } + + removeEffect (effect: StatusEffect): LogEntry { + this.effects = this.effects.filter(eff => eff !== effect) + return effect.onRemove(this) + } + + /** + * Determines how much damage an attack would do + */ + effectiveDamage (damage: Damage): Damage { + return this.effects.reduce((modifiedDamage: Damage, effect: StatusEffect) => { + return effect.preDamage(this, modifiedDamage) + }, damage) + } + get status (): Array { const results: Array = [] @@ -205,6 +154,30 @@ export class Creature extends Vore implements Combatant { return results } + executeAction (action: Action, target: Creature): LogEntry { + const effectResults = this.effects.map(effect => effect.preAction(this)) + const blocking = effectResults.filter(result => result.prevented) + if (blocking.length > 0) { + return new LogLines(...blocking.map(result => result.log)) + } else { + return action.execute(this, target) + } + } + + actions: Array = [] + groupActions: Array = [] + otherActions: Array = [] + items: Array = [] + + containedIn: VoreContainer|null = null; + + 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 + } + validActions (target: Creature): Array { let choices = this.actions.concat( this.containers.flatMap(container => container.actions) @@ -232,8 +205,4 @@ export class Creature extends Vore implements Combatant { return targets.some(target => action.allowed(this, target)) }) } - - destroy (): LogEntry { - return super.destroy() - } } diff --git a/src/game/vore.ts b/src/game/vore.ts index 1f1b007..f5e2d4c 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -1,5 +1,5 @@ import { Mortal } from './entity' -import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats, VisibleStatus } from './combat' +import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats, VisibleStatus, VoreStat } from './combat' import { LogLines, LogEntry, LogLine } from './interface' import { Noun, Pronoun, ImproperNoun, TextLike, Verb, SecondPersonPronouns, PronounAsNoun, FirstPersonPronouns, PairLineArgs, SoloLine, POV } from './language' import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions' @@ -12,50 +12,14 @@ export enum VoreType { Unbirth = "Unbirthing" } -export abstract class Vore implements Mortal { - abstract baseName: Noun; - abstract basePronouns: Pronoun; - abstract title: TextLike; - abstract desc: TextLike; - abstract kind: Noun; - abstract perspective: POV; - - 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 - } - } +export abstract class Vore extends Mortal { + destroyed = false; + voreStats: VoreStats - get pronouns (): Pronoun { - if (this.perspective === POV.First) { - return FirstPersonPronouns - } else if (this.perspective === POV.Second) { - return SecondPersonPronouns - } else { - return this.basePronouns - } - } + containedIn: Container | null = null + containers: Array = [] + otherContainers: Array = [] - abstract vigors: {[key in Vigor]: number}; - abstract maxVigors: {[key in Vigor]: number}; - abstract disabled: boolean; - abstract resistances: Map; - abstract effectiveDamage (damage: Damage): Damage; - abstract takeDamage (damage: Damage): LogEntry; - abstract stats: Stats; - abstract baseStats: Stats; - abstract status: VisibleStatus[]; - destroyed = false; - abstract preyPrefs: Set; - abstract voreStats: VoreStats; - abstract containedIn: Container | null; - abstract predPrefs: Set; - abstract containers: Array; - abstract otherContainers: Array; destroy (): LogEntry { const line: SoloLine = (victim) => new LogLine( `${victim.name.capital} ${victim.name.conjugate(new Verb('die'))}` @@ -89,6 +53,47 @@ export abstract class Vore implements Mortal { return line(this) } } + + constructor (name: Noun, kind: Noun, pronouns: Pronoun, baseStats: Stats, public preyPrefs: Set, public predPrefs: Set, public mass: number) { + super(name, kind, pronouns, baseStats) + + const containers = this.containers + + this.voreStats = { + get [VoreStat.Bulk] () { + return containers.reduce( + (total: number, container: VoreContainer) => { + return total + container.contents.reduce( + (total: number, prey: Vore) => { + return total + prey.voreStats.Bulk + }, + 0 + ) + container.digested.reduce( + (total: number, prey: Vore) => { + return total + prey.voreStats.Bulk + }, + 0 + ) + }, + this.Mass + ) + }, + [VoreStat.Mass]: mass, + get [VoreStat.PreyCount] () { + return containers.reduce( + (total: number, container: VoreContainer) => { + return total + container.contents.reduce( + (total: number, prey: Vore) => { + return total + 1 + prey.voreStats[VoreStat.PreyCount] + }, + 0 + ) + }, + 0 + ) + } + } + } } export interface Container extends Actionable {