Переглянути джерело

Add a status-effect system

master
Fen Dweller 5 роки тому
джерело
коміт
ce35c908d1
5 змінених файлів з 136 додано та 13 видалено
  1. +1
    -1
      src/components/ActionButton.vue
  2. +69
    -4
      src/game/combat.ts
  3. +26
    -2
      src/game/creatures/kenzie.ts
  4. +31
    -6
      src/game/entity.ts
  5. +9
    -0
      src/game/interface.ts

+ 1
- 1
src/components/ActionButton.vue Переглянути файл

@@ -31,7 +31,7 @@ export default class ActionButton extends Vue {
const action = (this.action as GroupAction)
this.$emit('executed', (this.action as GroupAction).executeGroup(this.user, action.allowedGroup(this.user, this.combatants)))
} else {
this.$emit('executed', this.action.execute(this.user, this.target))
this.$emit('executed', this.user.executeAction(this.action, this.target))
}
}



+ 69
- 4
src/game/combat.ts Переглянути файл

@@ -1,6 +1,6 @@
import { Creature } from './entity'
import { TextLike } from './language'
import { LogEntry, LogLines, FAElem, LogLine, FormatEntry, FormatOpt, PropElem } from './interface'
import { TextLike, DynText, ToBe } from './language'
import { LogEntry, LogLines, FAElem, LogLine, FormatEntry, FormatOpt, PropElem, nilLog } from './interface'

export enum DamageType {
Pierce = "Pierce",
@@ -327,10 +327,75 @@ export abstract class GroupAction extends Action {
}

/**
* A displayable status effect (whether implicit, like being dead, or explicit, like having a stun effect on you)
* A displayable status effect
*/
export class VisibleStatus {
export interface VisibleStatus {
name: TextLike;
desc: TextLike;
icon: TextLike;
}

/**
* This kind of status is never explicitly applied to an entity -- e.g., a dead entity will show
* a status indicating that it is dead, but entities cannot be "given" the dead effect
*/
export class ImplicitStatus implements VisibleStatus {
constructor (public name: TextLike, public desc: TextLike, public icon: string) {

}
}

/**
* This kind of status is explicitly given to a creature.
*
* Individual status effects should override some of its hooks.
* Some hooks just produce a log entry.
* Some hooks return results along with a log entry.
*/
export abstract class StatusEffect implements VisibleStatus {
constructor (public name: TextLike, public desc: TextLike, public icon: string) {

}

onApply (creature: Creature): LogEntry { return nilLog }
onRemove (creature: Creature): LogEntry { return nilLog }
preAction (creature: Creature): { prevented: boolean; log: LogEntry } {
return {
prevented: false,
log: nilLog
}
}
}

export class StunEffect extends StatusEffect {
constructor () {
super('Stun', 'Stunned!', 'fas fa-sun')
}

onApply (creature: Creature) {
return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} is stunned!`)
}

onRemove (creature: Creature) {
return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} no longer stunned.`)
}

preAction (creature: Creature): { prevented: boolean; log: LogEntry } {
if (Math.random() < 0.3) {
return {
prevented: true,
log: new LogLines(
`${creature.name.capital} ${creature.name.conjugate(new ToBe())} stunned! ${creature.pronouns.capital.subjective} can't move.`,
creature.removeEffect(this)
)
}
} else {
return {
prevented: true,
log: new LogLines(
`${creature.name.capital} ${creature.name.conjugate(new ToBe())} stunned! ${creature.pronouns.capital.subjective} can't move!`
)
}
}
}
}

+ 26
- 2
src/game/creatures/kenzie.ts Переглянути файл

@@ -1,9 +1,33 @@
import { Creature } from '../entity'
import { ProperNoun, ImproperNoun, FemalePronouns, Verb } from '../language'
import { VoreType, Stomach } from '../vore'
import { Side, Damage, DamageType, Vigor, StatDamageFormula, Stat, VoreStat } from '../combat'
import { Side, Damage, DamageType, Vigor, StatDamageFormula, Stat, VoreStat, DamageFormula, StunEffect } from '../combat'
import { AttackAction } from '../combat/actions'
import { LogEntry, LogLines } from '../interface'
import { StatTest } from '../combat/tests'

class StompAttack extends AttackAction {
execute (user: Creature, target: Creature): LogEntry {
if (this.test.test(user, target)) {
const damage = this.damage.calc(user, target)
const targetResult = target.takeDamage(damage)
const ownResult = this.successLine(user, target, { damage: damage })
const effResult = target.applyEffect(new StunEffect())
console.log(target.effects)
return new LogLines(ownResult, targetResult, effResult)
} else {
return this.failLine(user, target)
}
}

constructor (protected damage: DamageFormula, protected verb: Verb = new Verb('smack')) {
super(
damage,
verb
)
this.test = new StatTest(Stat.Power)
}
}
export class Kenzie extends Creature {
title = "Large Lycanroc"
desc = "Will eat your party"
@@ -30,7 +54,7 @@ export class Kenzie extends Creature {
this.containers.push(stomach)

this.actions.push(
new AttackAction(
new StompAttack(
new StatDamageFormula([
{ fraction: 0.5, stat: Stat.Toughness, target: Vigor.Health, type: DamageType.Crush },
{ fraction: 0.05, stat: VoreStat.Bulk, target: Vigor.Health, type: DamageType.Crush },


+ 31
- 6
src/game/entity.ts Переглянути файл

@@ -1,6 +1,6 @@
import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat, Side, GroupAction, Vigors, VisibleStatus } from './combat'
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 { LogEntry, LogLine } from './interface'
import { LogEntry, LogLine, LogLines } from './interface'
import { Vore, VoreContainer, VoreType, Container } from './vore'
import { Item } from './items'

@@ -48,6 +48,28 @@ export class Creature extends Vore implements Combatant {
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)
}
@@ -152,18 +174,21 @@ export class Creature extends Vore implements Combatant {
const results: Array<VisibleStatus> = []

if (this.vigors[Vigor.Health] <= 0) {
results.push(new VisibleStatus('Dead', 'Out of health', 'fas fa-heart'))
results.push(new ImplicitStatus('Dead', 'Out of health', 'fas fa-heart'))
}
if (this.vigors[Vigor.Stamina] <= 0) {
results.push(new VisibleStatus('Unconscious', 'Out of stamina', 'fas fa-bolt'))
results.push(new ImplicitStatus('Unconscious', 'Out of stamina', 'fas fa-bolt'))
}
if (this.vigors[Vigor.Resolve] <= 0) {
results.push(new VisibleStatus('Broken', 'Out of resolve', 'fas fa-brain'))
results.push(new ImplicitStatus('Broken', 'Out of resolve', 'fas fa-brain'))
}
if (this.containedIn !== null) {
results.push(new VisibleStatus('Eaten', 'Devoured by ' + this.containedIn.owner.name, 'fas fa-drumstick-bite'))
results.push(new ImplicitStatus('Eaten', 'Devoured by ' + this.containedIn.owner.name, 'fas fa-drumstick-bite'))
}

this.effects.forEach(effect => {
results.push(effect)
})
return results
}



+ 9
- 0
src/game/interface.ts Переглянути файл

@@ -230,3 +230,12 @@ export class CompositeLog implements LogEntry {
return this.entries.flatMap(e => e.render())
}
}

/**
* Represents nothing. Other elements should filter this out if they don't want blank spaces
*/
export const nilLog = {
render (): HTMLElement[] {
return []
}
}

Завантаження…
Відмінити
Зберегти