From c1c58d710dbaf22870b9f105bd41b6b70953530a Mon Sep 17 00:00:00 2001 From: Fen Dweller Date: Tue, 14 Jul 2020 19:43:11 -0400 Subject: [PATCH] Refactor some combat stuff; add effects --- src/game/combat.ts | 288 ++-------------------------------- src/game/combat/actions.ts | 258 ++++++++++++++++++++++++++++++ src/game/combat/conditions.ts | 27 ++++ src/game/combat/effects.ts | 18 +++ src/game/combat/tests.ts | 2 +- src/game/creatures/cafat.ts | 24 ++- src/game/creatures/player.ts | 3 +- src/game/creatures/wolf.ts | 9 +- src/game/interface.ts | 31 ++-- src/game/language.ts | 8 +- src/game/vore.ts | 113 ++++++------- 11 files changed, 414 insertions(+), 367 deletions(-) create mode 100644 src/game/combat/actions.ts create mode 100644 src/game/combat/conditions.ts create mode 100644 src/game/combat/effects.ts diff --git a/src/game/combat.ts b/src/game/combat.ts index 0e56bce..738a5a0 100644 --- a/src/game/combat.ts +++ b/src/game/combat.ts @@ -71,6 +71,12 @@ export interface CombatTest { explain: (user: Creature, target: Creature) => LogEntry; } +export interface Effect { + name: string; + desc: string; + apply: (target: Creature) => LogEntry; +} + export class Damage { readonly damages: DamageInstance[] @@ -172,36 +178,11 @@ export interface Condition { allowed: (user: Creature, target: Creature) => boolean; } -class InverseCondition implements Condition { - allowed (user: Creature, target: Creature): boolean { - return !this.condition.allowed(user, target) - } - - constructor (private condition: Condition) { - - } -} -class CapableCondition implements Condition { - allowed (user: Creature, target: Creature): boolean { - return !user.disabled - } -} - -class DrainedVigorCondition implements Condition { - allowed (user: Creature, target: Creature): boolean { - return user.vigors[this.vigor] <= 0 - } - - constructor (private vigor: Vigor) { - - } -} - export interface Actionable { actions: Array; } -abstract class SelfAction extends Action { +export abstract class SelfAction extends Action { allowed (user: Creature, target: Creature) { if (user === target) { return super.allowed(user, target) @@ -211,7 +192,7 @@ abstract class SelfAction extends Action { } } -abstract class PairAction extends Action { +export abstract class PairAction extends Action { allowed (user: Creature, target: Creature) { if (user !== target) { return super.allowed(user, target) @@ -221,7 +202,7 @@ abstract class PairAction extends Action { } } -abstract class TogetherAction extends PairAction { +export abstract class TogetherAction extends PairAction { allowed (user: Creature, target: Creature) { if (user.containedIn === target.containedIn) { return super.allowed(user, target) @@ -230,254 +211,3 @@ abstract class TogetherAction extends PairAction { } } } - -export class AttackAction extends TogetherAction { - protected test: StatTest - - protected successLines: POVPairArgs = new POVPairArgs([ - [[POV.First, POV.Third], (user, target, args) => new LogLine( - `You smack ${target.name} for `, - args.damage.renderShort() - )], - [[POV.Third, POV.First], (user, target, args) => new LogLine( - `${user.name.capital} smacks you for `, - args.damage.renderShort() - )], - [[POV.Third, POV.Third], (user, target, args) => new LogLine( - `${user.name.capital} smacks ${target.name} for `, - args.damage.renderShort() - )] - ]) - - protected failLines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You try to smack ${target.name}, but you miss`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} misses you`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} misses ${target.name}`)] - ]) - - constructor (protected damage: DamageFormula) { - super('Attack', 'Attack the enemy', [new CapableCondition()]) - this.test = new StatTest(Stat.Power) - } - - 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.successLines.run(user, target, { damage: damage }) - return new CompositeLog(ownResult, targetResult) - } else { - return this.failLines.run(user, target) - } - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Attack ${target.name}. `, this.damage.describe(user, target), '. ', this.test.explain(user, target)) - } -} - -export class DevourAction extends TogetherAction { - private test: StatVigorTest - - protected failLines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You fail to make a meal out of ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} tries to devour you, but fails`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} unsuccessfully tries to swallow ${target.name}`)] - ]) - - allowed (user: Creature, target: Creature): boolean { - const owner = this.container.owner === user - const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref)) - const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref)) - - if (owner && predOk && preyOk) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (protected container: Container) { - super(new DynText('Devour (', new LiveText(container, x => x.name.all), ')'), 'Try to consume your foe', [new CapableCondition()]) - this.test = new StatVigorTest(Stat.Power) - } - - execute (user: Creature, target: Creature): LogEntry { - if (this.test.test(user, target)) { - return this.container.consume(target) - } else { - return this.failLines.run(user, target) - } - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Try to consume your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target)) - } -} - -export class FeedAction extends TogetherAction { - private test: StatTest - - protected failLines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You fail to feed yourself to ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} tries to feed ${user.pronouns.possessive} to you, but fails`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} unsuccessfully tries to feed ${user.pronouns.possessive} to ${target.name}`)] - ]) - - allowed (user: Creature, target: Creature): boolean { - const owner = this.container.owner === target - const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref)) - const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref)) - - if (owner && predOk && preyOk) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (protected container: Container) { - super('Feed', 'Feed yourself to your opponent', [new DrainedVigorCondition(Vigor.Resolve)]) - this.name += ` (${container.name})` - this.test = new StatTest(Stat.Power) - } - - execute (user: Creature, target: Creature): LogEntry { - return this.container.consume(user) - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name}'s ${this.container.name}...`) - } -} - -export class StruggleAction extends PairAction { - private test: StatVigorTest - - protected failLines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You fail to escape from ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} tries to escape from you, but fails`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} unsuccessfully struggles within ${target.name}`)] - ]) - - allowed (user: Creature, target: Creature) { - if (user.containedIn === this.container && this.container.owner === target) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (public container: Container) { - super(new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'), 'Try to escape from your foe', [new CapableCondition()]) - this.test = new StatVigorTest(Stat.Power) - } - - execute (user: Creature, target: Creature): LogEntry { - if (user.containedIn !== null) { - if (this.test.test(user, target)) { - return user.containedIn.release(user) - } else { - return this.failLines.run(user, target) - } - } else { - return new LogLines("Vore's bugged!") - } - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Try to escape from ${target.name}'s ${this.container.name}. `, this.test.explain(user, target)) - } -} - -export abstract class EatenAction extends PairAction { - protected lines: POVPair = new POVPair([]) - - allowed (user: Creature, target: Creature) { - if (target.containedIn === this.container) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (public container: Container, name: TextLike, desc: string) { - super(new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), desc, [new CapableCondition()]) - } -} -export class DigestAction extends SelfAction { - protected lines: POVPair = new POVPair([]) - - allowed (user: Creature, target: Creature) { - if (this.container.owner === user && this.container.contents.length > 0) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (protected container: Container) { - super(new DynText('Digest (', new LiveText(container, container => container.name.all), ')'), 'Digest your prey', [new CapableCondition()]) - } - - execute (user: Creature, target: Creature): LogEntry { - const results = this.container.tick(60) - return new CompositeLog(results) - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Digest everyone inside of your ${this.container.name}.`) - } -} - -export class ReleaseAction extends PairAction { - allowed (user: Creature, target: Creature) { - if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (protected container: Container) { - super(new DynText('Release (', new LiveText(container, x => x.name.all), ')'), 'Release one of your prey', [new CapableCondition()]) - } - - execute (user: Creature, target: Creature): LogEntry { - return this.container.release(target) - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Release ${target.name} from your ${this.container.name}.`) - } -} - -export class TransferAction extends PairAction { - lines: POVPairArgs = new POVPairArgs([ - [[POV.First, POV.Third], (user, target, args) => new LogLine(`You squeeze ${target.name} from your ${args.from.name} to your ${args.to.name}`)], - [[POV.Third, POV.First], (user, target, args) => new LogLine(`You're squeezed from ${user.name}'s ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)], - [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name} squeezes ${target.name} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)] - ]) - - allowed (user: Creature, target: Creature) { - if (target.containedIn === this.from) { - return super.allowed(user, target) - } else { - return false - } - } - - constructor (protected from: Container, protected to: Container) { - super('Transfer', `Move from your ${from.name} to your ${to.name}`, [new CapableCondition()]) - } - - execute (user: Creature, target: Creature): LogEntry { - this.from.release(target) - this.to.consume(target) - return this.lines.run(user, target, { from: this.from, to: this.to }) - } - - describe (user: Creature, target: Creature): LogEntry { - return new LogLine(`Push ${target.name} from your ${this.from.name} to your ${this.to.name}`) - } -} diff --git a/src/game/combat/actions.ts b/src/game/combat/actions.ts new file mode 100644 index 0000000..6ef2ee2 --- /dev/null +++ b/src/game/combat/actions.ts @@ -0,0 +1,258 @@ +import { StatTest, StatVigorTest } from './tests' +import { POVPairArgs, POVPair, DynText, LiveText, TextLike } from '../language' +import { Entity, POV, Creature } from '../entity' +import { Damage, DamageFormula, Stat, Vigor, TogetherAction, PairAction, SelfAction } from '../combat' +import { LogLine, LogLines, LogEntry, CompositeLog } from '../interface' +import { Container } from '../vore' +import { CapableCondition, DrainedVigorCondition } from './conditions' + +export class AttackAction extends TogetherAction { + protected test: StatTest + + protected successLines: POVPairArgs = new POVPairArgs([ + [[POV.First, POV.Third], (user, target, args) => new LogLine( + `You smack ${target.name} for `, + args.damage.renderShort() + )], + [[POV.Third, POV.First], (user, target, args) => new LogLine( + `${user.name.capital} smacks you for `, + args.damage.renderShort() + )], + [[POV.Third, POV.Third], (user, target, args) => new LogLine( + `${user.name.capital} smacks ${target.name} for `, + args.damage.renderShort() + )] + ]) + + protected failLines: POVPair = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You try to smack ${target.name}, but you miss`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} misses you`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} misses ${target.name}`)] + ]) + + constructor (protected damage: DamageFormula) { + super('Attack', 'Attack the enemy', [new CapableCondition()]) + this.test = new StatTest(Stat.Power) + } + + 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.successLines.run(user, target, { damage: damage }) + return new CompositeLog(ownResult, targetResult) + } else { + return this.failLines.run(user, target) + } + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Attack ${target.name}. `, this.damage.describe(user, target), '. ', this.test.explain(user, target)) + } +} + +export class DevourAction extends TogetherAction { + private test: StatVigorTest + + protected failLines: POVPair = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You fail to make a meal out of ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} tries to devour you, but fails`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} unsuccessfully tries to swallow ${target.name}`)] + ]) + + allowed (user: Creature, target: Creature): boolean { + const owner = this.container.owner === user + const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref)) + const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref)) + + if (owner && predOk && preyOk) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (protected container: Container) { + super(new DynText('Devour (', new LiveText(container, x => x.name.all), ')'), 'Try to consume your foe', [new CapableCondition()]) + this.test = new StatVigorTest(Stat.Power) + } + + execute (user: Creature, target: Creature): LogEntry { + if (this.test.test(user, target)) { + return this.container.consume(target) + } else { + return this.failLines.run(user, target) + } + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Try to consume your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target)) + } +} + +export class FeedAction extends TogetherAction { + private test: StatTest + + protected failLines: POVPair = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You fail to feed yourself to ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} tries to feed ${user.pronouns.possessive} to you, but fails`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} unsuccessfully tries to feed ${user.pronouns.possessive} to ${target.name}`)] + ]) + + allowed (user: Creature, target: Creature): boolean { + const owner = this.container.owner === target + const predOk = Array.from(this.container.voreTypes).every(pref => user.predPrefs.has(pref)) + const preyOk = Array.from(this.container.voreTypes).every(pref => target.preyPrefs.has(pref)) + + if (owner && predOk && preyOk) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (protected container: Container) { + super('Feed', 'Feed yourself to your opponent', [new DrainedVigorCondition(Vigor.Resolve)]) + this.name += ` (${container.name})` + this.test = new StatTest(Stat.Power) + } + + execute (user: Creature, target: Creature): LogEntry { + return this.container.consume(user) + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name}'s ${this.container.name}...`) + } +} + +export class StruggleAction extends PairAction { + private test: StatVigorTest + + protected failLines: POVPair = new POVPair([ + [[POV.First, POV.Third], (user, target) => new LogLine(`You fail to escape from ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} tries to escape from you, but fails`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} unsuccessfully struggles within ${target.name}`)] + ]) + + allowed (user: Creature, target: Creature) { + if (user.containedIn === this.container && this.container.owner === target) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (public container: Container) { + super(new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'), 'Try to escape from your foe', [new CapableCondition()]) + this.test = new StatVigorTest(Stat.Power) + } + + execute (user: Creature, target: Creature): LogEntry { + if (user.containedIn !== null) { + if (this.test.test(user, target)) { + return user.containedIn.release(user) + } else { + return this.failLines.run(user, target) + } + } else { + return new LogLine("Vore's bugged!") + } + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Try to escape from ${target.name}'s ${this.container.name}. `, this.test.explain(user, target)) + } +} + +export abstract class EatenAction extends PairAction { + protected lines: POVPair = new POVPair([]) + + allowed (user: Creature, target: Creature) { + if (target.containedIn === this.container) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (public container: Container, name: TextLike, desc: string) { + super(new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), desc, [new CapableCondition()]) + } +} +export class DigestAction extends SelfAction { + protected lines: POVPair = new POVPair([]) + + allowed (user: Creature, target: Creature) { + if (this.container.owner === user && this.container.contents.length > 0) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (protected container: Container) { + super(new DynText('Digest (', new LiveText(container, container => container.name.all), ')'), 'Digest your prey', [new CapableCondition()]) + } + + execute (user: Creature, target: Creature): LogEntry { + const results = this.container.tick(60) + return new CompositeLog(results) + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Digest everyone inside of your ${this.container.name}.`) + } +} + +export class ReleaseAction extends PairAction { + allowed (user: Creature, target: Creature) { + if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (protected container: Container) { + super(new DynText('Release (', new LiveText(container, x => x.name.all), ')'), 'Release one of your prey', [new CapableCondition()]) + } + + execute (user: Creature, target: Creature): LogEntry { + return this.container.release(target) + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Release ${target.name} from your ${this.container.name}.`) + } +} + +export class TransferAction extends PairAction { + lines: POVPairArgs = new POVPairArgs([ + [[POV.First, POV.Third], (user, target, args) => new LogLine(`You squeeze ${target.name} from your ${args.from.name} to your ${args.to.name}`)], + [[POV.Third, POV.First], (user, target, args) => new LogLine(`You're squeezed from ${user.name}'s ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)], + [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name} squeezes ${target.name} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}`)] + ]) + + allowed (user: Creature, target: Creature) { + if (target.containedIn === this.from) { + return super.allowed(user, target) + } else { + return false + } + } + + constructor (protected from: Container, protected to: Container) { + super('Transfer', `Move from your ${from.name} to your ${to.name}`, [new CapableCondition()]) + } + + execute (user: Creature, target: Creature): LogEntry { + this.from.release(target) + this.to.consume(target) + return this.lines.run(user, target, { from: this.from, to: this.to }) + } + + describe (user: Creature, target: Creature): LogEntry { + return new LogLine(`Push ${target.name} from your ${this.from.name} to your ${this.to.name}`) + } +} diff --git a/src/game/combat/conditions.ts b/src/game/combat/conditions.ts new file mode 100644 index 0000000..305caa3 --- /dev/null +++ b/src/game/combat/conditions.ts @@ -0,0 +1,27 @@ +import { Condition, Vigor } from "../combat" +import { Creature } from "../entity" + +export class InverseCondition implements Condition { + allowed (user: Creature, target: Creature): boolean { + return !this.condition.allowed(user, target) + } + + constructor (private condition: Condition) { + + } +} +export class CapableCondition implements Condition { + allowed (user: Creature, target: Creature): boolean { + return !user.disabled + } +} + +export class DrainedVigorCondition implements Condition { + allowed (user: Creature, target: Creature): boolean { + return user.vigors[this.vigor] <= 0 + } + + constructor (private vigor: Vigor) { + + } +} diff --git a/src/game/combat/effects.ts b/src/game/combat/effects.ts new file mode 100644 index 0000000..255fc76 --- /dev/null +++ b/src/game/combat/effects.ts @@ -0,0 +1,18 @@ +import { Creature, POV } from '../entity' +import { LogEntry, LogLine, FAElem } from '../interface' +import { Effect } from '../combat' +import { POVSolo } from '../language' + +export class InstantKill implements Effect { + lines = new POVSolo([ + [[POV.First], (target: Creature) => new LogLine(`You're killed instantly! `, new FAElem('fas fa-skull'))], + [[POV.Third], (target: Creature) => new LogLine(`${target.name.capital} is killed instantly! `, new FAElem('fas fa-skull'))] + ]) + + name = "Instant Kill" + desc = "Instantly kills its victim" + apply (target: Creature): LogEntry { + target.vigors.Health = Math.min(0, target.vigors.Health) + return this.lines.run(target) + } +} diff --git a/src/game/combat/tests.ts b/src/game/combat/tests.ts index c581471..ea46438 100644 --- a/src/game/combat/tests.ts +++ b/src/game/combat/tests.ts @@ -128,6 +128,6 @@ export class ChanceTest extends RandomTest { } explain (user: Creature, target: Creature): LogEntry { - return new LogLines('You have a flat ' + (100 * this.chance) + '% chance.') + return new LogLine('You have a flat ' + (100 * this.chance) + '% chance.') } } diff --git a/src/game/creatures/cafat.ts b/src/game/creatures/cafat.ts index b47e15d..e97a03b 100644 --- a/src/game/creatures/cafat.ts +++ b/src/game/creatures/cafat.ts @@ -1,8 +1,10 @@ import { Creature, POV, Entity } from '../entity' -import { Stat, Damage, DamageType, TransferAction, Vigor, FeedAction, EatenAction, AttackAction, ConstantDamageFormula } from '../combat' +import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula } from '../combat' import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, POVPairArgs } from '../language' import { VoreType, Stomach, InnerStomach, Container } from '../vore' import { LogLine, LogLines, LogEntry, FAElem, CompositeLog, ImgElem } from '../interface' +import { AttackAction, EatenAction, TransferAction, FeedAction } from '../combat/actions' +import { InstantKill } from '../combat/effects' class BellyCrushAction extends AttackAction { successLines = new POVPairArgs([ @@ -56,25 +58,21 @@ class BelchAction extends AttackAction { this.desc = 'Drain your foe\'s willpower with a solid BELCH' } } + class CrushAction extends EatenAction { lines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLine(`You crush ${target.name} `, new FAElem('fas fa-skull'))], - [[POV.Third, POV.First], (user) => new CompositeLog(new LogLine(`${user.name.capital} crushes you; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, new FAElem('fas fa-skull')), new ImgElem('./media/cafat/images/crunch.webp'))], - [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} crushes ${target.name}; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, new FAElem('fas fa-skull'))] + [[POV.First, POV.Third], (user, target) => new LogLine(`You crush ${target.name} `)], + [[POV.Third, POV.First], (user) => new CompositeLog(new LogLine(`${user.name.capital} crushes you; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `), new ImgElem('./media/cafat/images/crunch.webp'))], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} crushes ${target.name}; ${user.pronouns.subjective} ${user.pronouns.isPlural ? 'belch' : 'belches'} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `)] ]) - private damage: Damage = new Damage( - { amount: 99, type: DamageType.Crush, target: Vigor.Health } - ) - constructor (container: Container) { super(container, "Crush", "Crush 'em!") this.desc = "Crush somebody in your gut" } execute (user: Creature, target: Creature): LogEntry { - target.takeDamage(this.damage) - return this.lines.run(user, target) + return new LogLines(this.lines.run(user, target), new InstantKill().apply(target)) } describe (user: Creature, target: Creature): LogEntry { @@ -118,9 +116,9 @@ export class Cafat extends Creature { lowerStomach.name = new ImproperNoun("lower stomach", "lower stomachs").all stomach.consumeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You devour ${target.name}`)], - [[POV.Third, POV.First], (user) => new CompositeLog(new LogLines(`${user.name.capital} devours you`), new ImgElem('./media/cafat/images/stomach.webp'))], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} munches ${target.name.capital}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)], + [[POV.Third, POV.First], (user) => new CompositeLog(new LogLine(`${user.name.capital} devours you`), new ImgElem('./media/cafat/images/stomach.webp'))], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name.capital}`)] ]) const crush = new CrushAction(lowerStomach) diff --git a/src/game/creatures/player.ts b/src/game/creatures/player.ts index ef95e79..9d44c65 100644 --- a/src/game/creatures/player.ts +++ b/src/game/creatures/player.ts @@ -1,7 +1,8 @@ import { Creature, POV } from '../entity' import { ProperNoun, TheyPronouns } from '../language' -import { Stat, Damage, AttackAction, DamageType, Vigor, ConstantDamageFormula } from '../combat' +import { Stat, Damage, DamageType, Vigor, ConstantDamageFormula } from '../combat' import { Stomach, Bowels, VoreType } from '../vore' +import { AttackAction } from '../combat/actions' export class Player extends Creature { constructor () { diff --git a/src/game/creatures/wolf.ts b/src/game/creatures/wolf.ts index 50fb931..980f03b 100644 --- a/src/game/creatures/wolf.ts +++ b/src/game/creatures/wolf.ts @@ -1,9 +1,10 @@ import { Creature, POV, Entity } from '../entity' -import { Stat, Damage, DamageType, AttackAction, TransferAction, Vigor, FeedAction, ConstantDamageFormula } from '../combat' +import { Stat, Damage, DamageType, ConstantDamageFormula, Vigor } from '../combat' import { MalePronouns, ImproperNoun, POVPair, POVPairArgs } from '../language' import { LogLine, LogLines } from '../interface' import { VoreType, Stomach, Bowels } from '../vore' import { StatTest } from '../combat/tests' +import { AttackAction, TransferAction, FeedAction } from '../combat/actions' class BiteAction extends AttackAction { constructor () { @@ -29,9 +30,9 @@ class HypnoAction extends AttackAction { ]) protected failLines: POVPair = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You try to hypnotize ${target.name}, but you miss`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} misses you`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name} misses ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You try to hypnotize ${target.name}, but you miss`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} misses you`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name} misses ${target.name}`)] ]) constructor () { diff --git a/src/game/interface.ts b/src/game/interface.ts index 022c155..8ea4317 100644 --- a/src/game/interface.ts +++ b/src/game/interface.ts @@ -5,18 +5,28 @@ export interface LogEntry { } export class LogLines implements LogEntry { - lines: string[] + private parts: Array - constructor (...lines: string[]) { - this.lines = lines + constructor (...parts: Array) { + this.parts = parts } render (): HTMLElement[] { - return this.lines.map(line => { - const div = document.createElement("div") - div.innerText = line - return div + const div = document.createElement("div") + + this.parts.forEach(part => { + if (typeof part === "string") { + const partDiv = document.createElement("span") + partDiv.innerText = part + div.appendChild(partDiv) + } else { + (part as LogEntry).render().forEach(logPart => { + div.appendChild(logPart) + }) + } }) + + return [div] } } @@ -114,8 +124,11 @@ export class PropElem implements LogEntry { const span = document.createElement("span") span.classList.add("stat-entry") - span.dataset.tooltip = this.prop - + if (this.prop in Stat) { + span.dataset.tooltip = this.prop + } else if (this.prop in Vigor) { + span.dataset.tooltip = this.prop + } if (this.prop in Stat) { span.dataset.tooltipFull = StatDescs[this.prop as Stat] } else if (this.prop in Vigor) { diff --git a/src/game/language.ts b/src/game/language.ts index 7638121..969ec78 100644 --- a/src/game/language.ts +++ b/src/game/language.ts @@ -1,12 +1,12 @@ import { Entity, POV } from './entity' -import { LogEntry, LogLines } from './interface' +import { LogEntry, LogLines, LogLine } from './interface' export class POVPair { run (user: K, target: V): LogEntry { const choice = this.options.find(element => element[0][0] === user.perspective && element[0][1] === target.perspective) if (choice === undefined) { - return new LogLines("Fen didn't write any text for this...") + return new LogLine("Fen didn't write any text for this...") } else { return choice[1](user, target) } @@ -22,7 +22,7 @@ export class POVPairArgs { const choice = this.options.find(element => element[0][0] === user.perspective && element[0][1] === target.perspective) if (choice === undefined) { - return new LogLines("Fen didn't write any text for this...") + return new LogLine("Fen didn't write any text for this...") } else { return choice[1](user, target, args) } @@ -37,7 +37,7 @@ export class POVSolo { const choice = this.options.find(element => element[0][0] === user.perspective) if (choice === undefined) { - return new LogLines("Fen didn't write any text for this...") + return new LogLine("Fen didn't write any text for this...") } else { return choice[1](user, args) } diff --git a/src/game/vore.ts b/src/game/vore.ts index 6c8f1c2..b08a186 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -1,7 +1,8 @@ import { Entity, Mortal, POV, Creature } from './entity' -import { Damage, DamageType, Stats, Actionable, Action, DevourAction, FeedAction, DigestAction, ReleaseAction, StruggleAction, Vigor } from './combat' +import { Damage, DamageType, Stats, Actionable, Action, Vigor } from './combat' import { LogLines, LogEntry, CompositeLog, LogLine } from './interface' import { Noun, Pronoun, POVPair, POVPairArgs, ImproperNoun } from './language' +import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions' export enum VoreType { Oral = "Oral Vore", @@ -142,7 +143,7 @@ abstract class NormalContainer implements Container { lines.push(prey.toString()) }) - return new LogLines(...lines) + return new LogLine(...lines) } digest (prey: Vore): LogEntry { @@ -196,21 +197,21 @@ export class Stomach extends NormalContainer { } consumeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You devour ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} munches you`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} munches ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name}`)] ]) releaseLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You hork up ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} horks you up`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} horks up ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name}`)] ]) struggleLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You claw your way out of ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} escapes from the gut of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)] ]) tickLines = new POVPairArgs([ @@ -220,21 +221,21 @@ export class Stomach extends NormalContainer { ]) digestLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your stomach overwhelms ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s stomach finishes you off`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)] ]) absorbLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) } @@ -244,21 +245,21 @@ export class InnerStomach extends InnerContainer { } consumeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You devour ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} munches you`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} munches ${target.name.capital}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name.capital}`)] ]) releaseLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You hork up ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} horks you up`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} horks up ${target.name.capital}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name.capital}`)] ]) struggleLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You claw your way out of ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} escapes from the gut of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)] ]) tickLines = new POVPairArgs([ @@ -268,21 +269,21 @@ export class InnerStomach extends InnerContainer { ]) digestLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your stomach overwhelms ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s stomach finishes you off`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)] ]) absorbLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) } @@ -292,21 +293,21 @@ export class Bowels extends NormalContainer { } consumeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You force ${target.name} into your bowels`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} works you into ${user.pronouns.possessive} ass`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} anal-vores ${target.name.capital}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You force ${target.name} into your bowels`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} works you into ${user.pronouns.possessive} ass`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} anal-vores ${target.name.capital}`)] ]) releaseLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You let out ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} lets you out `)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} lets out ${target.name.capital}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You let out ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} lets you out `)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} lets out ${target.name.capital}`)] ]) struggleLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`You claw your way out of ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} forces ${user.pronouns.possessive} way out your rump!`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} escapes from the bowels of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way out your rump!`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the bowels of ${target.name}`)] ]) tickLines = new POVPairArgs([ @@ -316,20 +317,20 @@ export class Bowels extends NormalContainer { ]) digestLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your bowels overwhelm ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s bowels finish you off`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name.capital}'s squirms fade, overwhelmed by the bowels of ${user.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your bowels overwhelm ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s bowels finish you off`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the bowels of ${user.name}`)] ]) absorbLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) disposeLines = new POVPair([ - [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)], - [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)], - [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] + [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)], + [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)], + [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)] ]) }