|
- import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat, VoreStats } from './combat'
- import { Noun, Pronoun, SoloLine, Verb } from './language'
- import { LogEntry, LogLines, LogLine } from './interface'
- import { VoreContainer, VoreType, Container } from './vore'
- import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from './items'
- import { PassAction } from './combat/actions'
- import { AI } from './ai'
- import { Mortal } from './entity'
- import { Perk } from './combat/perks'
-
- export class Creature extends Mortal {
- containers: Array<VoreContainer> = []
- otherContainers: Array<Container> = []
-
- containedIn: Container | null = null
-
- voreStats: VoreStats
-
- actions: Array<Action> = [];
- 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
- ),
- this.perks
- )
- }
-
- statusEffects: Array<StatusEffect> = [];
- perks: Array<Perk> = [];
-
- groupActions: Array<GroupAction> = [];
- items: Array<Item> = [];
- /* eslint-disable-next-line */
- wallet: { [key in Currency]: number } = Object.keys(Currency).reduce((total: any, key) => { total[key] = 0; return total }, {});
- otherActions: Array<Action> = [];
- side: Side;
- title = "Lv. 1 Creature";
- equipment: {[key in EquipmentSlot]?: Equipment } = {}
- ai: AI|null = null
-
- constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, public preyPrefs: Set<VoreType>, public predPrefs: Set<VoreType>, private baseMass: number) {
- super(name, kind, pronouns, stats)
-
- this.actions.push(new PassAction())
- this.side = Side.Heroes
- /* eslint-disable-next-line */
- const self = this
-
- this.voreStats = {
- get [VoreStat.Bulk] () {
- return self.containers.reduce(
- (total: number, container: VoreContainer) => {
- return total + container.contents.reduce(
- (total: number, prey: Creature) => {
- return total + prey.voreStats.Bulk
- },
- 0
- ) + container.digested.reduce(
- (total: number, prey: Creature) => {
- return total + prey.voreStats.Bulk
- },
- 0
- )
- },
- self.voreStats.Mass
- )
- },
- get [VoreStat.Mass] () {
- const base = self.baseMass
- const adjusted = self.effects.reduce((scale: number, effect: Effective) => effect.scale(scale), base)
- return adjusted
- },
- // we want to account for anything changing our current size;
- // we will assume that the modifiers are all multiplicative
- set [VoreStat.Mass] (mass: number) {
- const modifier = self.effects.reduce((scale: number, effect: Effective) => effect.scale(scale), 1)
- const adjusted = mass / modifier
- self.baseMass = adjusted
- },
- get [VoreStat.PreyCount] () {
- return self.containers.reduce(
- (total: number, container: VoreContainer) => {
- return total + container.contents.concat(container.digested).reduce(
- (total: number, prey: Creature) => {
- return total + 1 + prey.voreStats[VoreStat.PreyCount]
- },
- 0
- )
- },
- 0
- )
- }
- }
- }
-
- 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.try(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
- }
-
- allActions (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
- }
-
- validActions (target: Creature): Array<Action> {
- return this.allActions(target).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))
- })
- }
-
- destroy (): LogEntry {
- const line: SoloLine<Creature> = (victim) => new LogLine(
- `${victim.name.capital} ${victim.name.conjugate(new Verb('die'))}`
- )
-
- const released: Array<Creature> = this.containers.flatMap(container => {
- return container.contents.map(prey => {
- prey.containedIn = this.containedIn
- if (this.containedIn !== null) {
- this.containedIn.contents.push(prey)
- }
- return prey
- })
- })
-
- const names = released.reduce((list: Array<string>, prey: Creature) => list.concat([prey.name.toString()]), []).joinGeneral(", ", " and ").join("")
-
- if (released.length > 0) {
- if (this.containedIn === null) {
- return new LogLines(
- line(this),
- new LogLine(names + ` spill out!`)
- )
- } else {
- return new LogLines(
- line(this),
- new LogLine(names + ` spill out into ${this.containedIn.owner.name}'s ${this.containedIn.name}!`)
- )
- }
- } else {
- return line(this)
- }
- }
- }
|