Parcourir la source

Refactor Entity and Mortal into abstract classes; rearrange Vore and Entity

vintage
Fen Dweller il y a 5 ans
Parent
révision
cb07582a26
3 fichiers modifiés avec 149 ajouts et 175 suppressions
  1. +3
    -3
      src/game/creatures/cafat.ts
  2. +98
    -129
      src/game/entity.ts
  3. +48
    -43
      src/game/vore.ts

+ 3
- 3
src/game/creatures/cafat.ts Voir le fichier

@@ -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!'


+ 98
- 129
src/game/entity.ts Voir le fichier

@@ -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<DamageType, number>;
takeDamage: (damage: Damage) => void;
export abstract class Mortal extends Entity {
abstract destroy (): LogEntry;
abstract effectiveDamage (damage: Damage): Damage
resistances: Map<DamageType, number> = 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<StatusEffect> = []

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<DamageType, number> = new Map()
perspective: POV = POV.Third
containers: Array<VoreContainer> = []
otherContainers: Array<Container> = []
actions: Array<Action> = []
groupActions: Array<GroupAction> = []
otherActions: Array<Action> = []

items: Array<Item> = []

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<VoreType>, public predPrefs: Set<VoreType>, 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<StatusEffect> = []

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<VisibleStatus> {
const results: Array<VisibleStatus> = []

@@ -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<Action> = []
groupActions: Array<GroupAction> = []
otherActions: Array<Action> = []
items: Array<Item> = []

containedIn: VoreContainer|null = null;

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
}

validActions (target: Creature): Array<Action> {
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()
}
}

+ 48
- 43
src/game/vore.ts Voir le fichier

@@ -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<VoreContainer> = []
otherContainers: Array<Container> = []

abstract vigors: {[key in Vigor]: number};
abstract maxVigors: {[key in Vigor]: number};
abstract disabled: boolean;
abstract resistances: Map<DamageType, number>;
abstract effectiveDamage (damage: Damage): Damage;
abstract takeDamage (damage: Damage): LogEntry;
abstract stats: Stats;
abstract baseStats: Stats;
abstract status: VisibleStatus[];
destroyed = false;
abstract preyPrefs: Set<VoreType>;
abstract voreStats: VoreStats;
abstract containedIn: Container | null;
abstract predPrefs: Set<VoreType>;
abstract containers: Array<VoreContainer>;
abstract otherContainers: Array<Container>;
destroy (): LogEntry {
const line: SoloLine<Vore> = (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<VoreType>, public predPrefs: Set<VoreType>, 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 {


Chargement…
Annuler
Enregistrer