From c2681a26688756fb2d78323439d58cab3839ca62 Mon Sep 17 00:00:00 2001 From: Fen Dweller Date: Fri, 17 Jun 2022 09:41:29 -0400 Subject: [PATCH] Add onomatopoeia, separate entering from consuming Containers have separate methods for consuming prey and having prey enter them: the latter is used for transfers. Also makes the left-side characters align to the right. I forget why I didn't do that; it was probably a browser bug. Also lets you become a werewolf. --- src/App.vue | 14 +++++ src/components/Combat.vue | 2 +- src/game/combat/actions.ts | 11 ++-- src/game/creatures/monsters/werewolf.ts | 1 + src/game/events.ts | 4 +- src/game/interface.ts | 3 +- src/game/language.ts | 26 +++++++++ src/game/maps/town.ts | 16 +++++- src/game/onomatopoeia.ts | 58 ++++++++++++++++++++ src/game/vore.ts | 72 +++++++++++++++++++++---- 10 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 src/game/onomatopoeia.ts diff --git a/src/App.vue b/src/App.vue index 1339cb4..571ddaf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -193,4 +193,18 @@ html { /* .component-fade-leave-active below version 2.1.8 */ { opacity: 0; } + +.onomatopoeia { + font-weight: bold; + font-style: italic; + font-size: 200%; + animation: fly-in 0.4s; + animation-timing-function: ease-out; + display: inline-block; +} + +@keyframes fly-in { + 0% { transform: scale(0, 0); opacity: 0 }; + 100% { transform: scale(1, 1); opacity: 1 }; +} diff --git a/src/components/Combat.vue b/src/components/Combat.vue index d0c0a3a..071e31c 100644 --- a/src/components/Combat.vue +++ b/src/components/Combat.vue @@ -386,7 +386,7 @@ export default class Combat extends Vue { } .left-stats { - flex-direction: row; + flex-direction: row-reverse; } .right-stats { diff --git a/src/game/combat/actions.ts b/src/game/combat/actions.ts index 0ace451..84339b5 100644 --- a/src/game/combat/actions.ts +++ b/src/game/combat/actions.ts @@ -338,9 +338,14 @@ export class TransferAction extends Action { } execute (user: Creature, target: Creature): LogEntry { - this.from.release(target) - this.to.destination.consume(target) - return this.line(user, target, { from: this.from, to: this.to }) + const results = [ + this.from.exit(target), + this.line(user, target, { from: this.from, to: this.to }), + this.to.destination.enter(target) + + ] + + return new LogLines(...results) } describe (user: Creature, target: Creature): LogEntry { diff --git a/src/game/creatures/monsters/werewolf.ts b/src/game/creatures/monsters/werewolf.ts index 532befb..1b1139b 100644 --- a/src/game/creatures/monsters/werewolf.ts +++ b/src/game/creatures/monsters/werewolf.ts @@ -1,5 +1,6 @@ import { VoreAI } from '@/game/ai' import { DamageType, Side, Stat, StatDamageFormula, Vigor } from '@/game/combat' +import { DigestionPowerEffect } from '@/game/combat/effects' import { Creature } from '@/game/creature' import { LogEntry, LogLine } from '@/game/interface' import { ImproperNoun, MalePronouns, ObjectPronouns, Preposition, Verb } from '@/game/language' diff --git a/src/game/events.ts b/src/game/events.ts index 9fa8b6e..9f2de74 100644 --- a/src/game/events.ts +++ b/src/game/events.ts @@ -43,13 +43,15 @@ abstract class Relay { type VoreMap = { "onEaten": { prey: Creature }; "onReleased": { prey: Creature }; + "onEntered": { prey: Creature }; + "onExited": { prey: Creature }; "onDigested": { prey: Creature }; "onAbsorbed": { prey: Creature }; } export class VoreRelay extends Relay { constructor () { - super(["onEaten", "onReleased", "onDigested", "onAbsorbed"]) + super(["onEaten", "onReleased", "onEntered", "onExited", "onDigested", "onAbsorbed"]) } } diff --git a/src/game/interface.ts b/src/game/interface.ts index aafc708..9eda603 100644 --- a/src/game/interface.ts +++ b/src/game/interface.ts @@ -55,7 +55,8 @@ export class LogLines implements LogEntry { export enum FormatOpt { Damage = "log-damage", - DamageInst = "damage-instance" + DamageInst = "damage-instance", + Onomatopoeia = "onomatopoeia" } /** diff --git a/src/game/language.ts b/src/game/language.ts index f15b388..c42e3e6 100644 --- a/src/game/language.ts +++ b/src/game/language.ts @@ -1,4 +1,5 @@ import { LogEntry } from "@/game/interface" +import { parseTwoDigitYear } from "moment" export enum POV { First, @@ -574,6 +575,31 @@ export class Pronoun implements Pluralizable { } } +export type OnomatopoeiaPart = [string, number, number] + +export class Onomatopoeia extends Word { + constructor (protected parts: Array, public opts: WordOptions = emptyConfig) { + super(opts) + } + + configure (opts: WordOptions): Word { + return new Onomatopoeia(this.parts, opts) + } + + toString (): string { + const built = this.parts.reduce((result, next) => { + const [piece, min, max] = next + const count = Math.floor(Math.random() * (max - min)) + min + for (let i = 0; i < count; i++) { + result += piece + } + return result + }, "") + + return built + } +} + export const MalePronouns = new Pronoun({ subjective: "he", objective: "him", diff --git a/src/game/maps/town.ts b/src/game/maps/town.ts index 891fdd7..64af86d 100644 --- a/src/game/maps/town.ts +++ b/src/game/maps/town.ts @@ -1,5 +1,5 @@ import { Place, Choice, Direction, World } from '@/game/world' -import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns, TheyPronouns } from '@/game/language' +import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns, TheyPronouns, POV } from '@/game/language' import { Encounter, Stat, Damage, DamageType, Vigor, Side } from '@/game/combat' import * as Items from '@/game/items' import { LogLine, nilLog, LogLines } from '@/game/interface' @@ -148,6 +148,20 @@ export const Town = (): Place => { ) ) + home.choices.push( + new Choice( + "Become a werewolf", + "Yum", + (world, executor) => { + world.player = new Werewolf() + world.player.location = home + world.player.perspective = POV.Second + world.player.side = Side.Heroes + return new LogLine("Nice") + } + ) + ) + square.choices.push( new Choice( "Eat someone", diff --git a/src/game/onomatopoeia.ts b/src/game/onomatopoeia.ts new file mode 100644 index 0000000..891aaa6 --- /dev/null +++ b/src/game/onomatopoeia.ts @@ -0,0 +1,58 @@ +import { Onomatopoeia, RandomWord } from "./language" + +export const Swallow = new RandomWord([ + new Onomatopoeia([ + ["GL", 1, 1], + ["U", 1, 10], + ["R", 1, 3], + ["K", 1, 1], + ["!", 1, 3] + ]), + new Onomatopoeia([ + ["GL", 1, 1], + ["U", 1, 3], + ["NK", 1, 1], + ["!", 1, 3] + ]), + new Onomatopoeia([ + ["G", 1, 1], + ["L", 1, 3], + ["U", 1, 3], + ["RSH", 1, 1], + ["!", 1, 3] + ]) +]) + +export const Glunk = new RandomWord([ + new Onomatopoeia([ + ["G", 1, 1], + ["L", 2, 4], + ["U", 1, 3], + ["NK", 1, 1], + ["!", 1, 1] + ]), + new Onomatopoeia([ + ["GL", 1, 1], + ["O", 2, 5], + ["RSH", 1, 1], + ["!", 1, 1] + ]) +]) + +export const Gurgle = new RandomWord([ + new Onomatopoeia([ + ["g", 1, 1], + ["w", 1, 3], + ["o", 1, 3], + ["r", 1, 2], + ["b", 1, 3], + ["le", 1, 1] + ]), + new Onomatopoeia([ + ["g", 2, 5], + ["r", 3, 7], + ["rg", 1, 1], + ["l", 1, 3], + ["e", 1, 1] + ]) +]) diff --git a/src/game/vore.ts b/src/game/vore.ts index f963941..f21d8fe 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -1,8 +1,9 @@ import { Damage, DamageType, Actionable, Action, Vigor, DamageInstance, DamageFormula, ConstantDamageFormula } from '@/game/combat' -import { LogLines, LogEntry, LogLine, nilLog, RandomEntry } from '@/game/interface' +import { LogLines, LogEntry, LogLine, nilLog, RandomEntry, FormatEntry, FormatOpt } from '@/game/interface' import { Noun, ImproperNoun, Verb, RandomWord, Word, Preposition, ToBe, Adjective } from '@/game/language' import { RubAction, DevourAction, ReleaseAction, StruggleAction, TransferAction, StruggleMoveAction } from '@/game/combat/actions' import * as Words from '@/game/words' +import * as Onomatopoeia from '@/game/onomatopoeia' import { Creature } from '@/game/creature' import { VoreRelay } from '@/game/events' @@ -87,8 +88,13 @@ export interface Container extends Actionable { strugglePreposition: Preposition; canTake (prey: Creature): boolean; + consume (prey: Creature): LogEntry; release (prey: Creature): LogEntry; + + enter (prey: Creature): LogEntry; + exit (prey: Creature): LogEntry; + struggle (prey: Creature): LogEntry; tick (dt: number, victims?: Array): LogEntry; @@ -189,29 +195,57 @@ export abstract class DefaultContainer implements Container { } consume (prey: Creature): LogEntry { + const results: Array = [ + this.enter(prey), + this.voreRelay.dispatch("onEaten", this, { prey: prey }), + prey.voreRelay.dispatch("onEaten", this, { prey: prey }), + this.consumeLine(this.owner, prey) + ] + + this.owner.effects.forEach(effect => results.push(effect.postConsume(this.owner, prey, this))) + + return new LogLines(...results) + } + + release (prey: Creature): LogEntry { + const results = [ + this.exit(prey), + this.releaseLine(this.owner, prey), + this.voreRelay.dispatch("onReleased", this, { prey: prey }), + prey.voreRelay.dispatch("onReleased", this, { prey: prey }) + ] + return new LogLines(...results) + } + + enter (prey: Creature): LogEntry { if (prey.containedIn !== null) { prey.containedIn.contents = prey.containedIn.contents.filter(item => prey !== item) } this.contents.push(prey) prey.containedIn = this - const results: Array = [] - this.owner.effects.forEach(effect => results.push(effect.postConsume(this.owner, prey, this))) - const relayResults = this.voreRelay.dispatch("onEaten", this, { prey: prey }) - const preyRelayResults = prey.voreRelay.dispatch("onEaten", this, { prey: prey }) - const consumeLineResult: LogEntry = this.consumeLine(this.owner, prey) + const results = [ + this.voreRelay.dispatch("onEntered", this, { prey: prey }), + prey.voreRelay.dispatch("onEntered", this, { prey: prey }) + ] - return new LogLines(...[consumeLineResult].concat(results).concat(relayResults).concat(preyRelayResults)) + return new LogLines(...results) } - release (prey: Creature): LogEntry { + exit (prey: Creature): LogEntry { prey.containedIn = this.owner.containedIn this.contents = this.contents.filter(victim => victim !== prey) if (this.owner.containedIn !== null) { this.owner.containedIn.contents.push(prey) } - return new LogLines(this.releaseLine(this.owner, prey), this.voreRelay.dispatch("onReleased", this, { prey: prey })) + + const results = [ + this.voreRelay.dispatch("onExited", this, { prey: prey }), + prey.voreRelay.dispatch("onExited", this, { prey: prey }) + ] + + return new LogLines(...results) } struggle (prey: Creature): LogEntry { @@ -268,7 +302,15 @@ export abstract class DefaultContainer implements Container { options.push(new LogLine(`${this.fluid.name.capital} ${this.fluid.sloshVerb.singular} and ${this.fluid.sound.singular} as ${this.owner.name.possessive} ${this.name} steadily ${Words.Digest.singular} ${target.name.objective}.`)) } - return new RandomEntry(...options) + const result: Array = [ + new RandomEntry(...options) + ] + + if (Math.random() < 0.3) { + result.push(new FormatEntry(new LogLine(`${Onomatopoeia.Gurgle}`), FormatOpt.Onomatopoeia)) + } + + return new LogLines(...result) } digestLine (user: Creature, target: Creature): LogEntry { @@ -390,6 +432,10 @@ export class Stomach extends DefaultContainer { ContainerCapability.Absorb ])) + this.voreRelay.subscribe("onEntered", (sender: Container, args: { prey: Creature }) => { + return new FormatEntry(new LogLine(`${Onomatopoeia.Glunk}`), FormatOpt.Onomatopoeia) + }) + this.damage = damage } } @@ -418,11 +464,15 @@ export class Throat extends DefaultContainer { ContainerCapability.Consume, ContainerCapability.Release ])) + + this.voreRelay.subscribe("onEaten", (sender: Container, args: { prey: Creature }) => { + return new FormatEntry(new LogLine(`${Onomatopoeia.Swallow}`), FormatOpt.Onomatopoeia) + }) } } export function transferDescription (verb: Word, preposition: Preposition): ((from: Container, to: Container, prey: Creature) => LogEntry) { return (from: Container, to: Container, prey: Creature) => { - return new LogLine(`${from.owner.name.capital} ${verb.singular} ${prey.name.objective} ${preposition} ${to.consumePreposition} ${from.owner.pronouns.possessive} ${to.name}.`) + return new LogLine(`${from.owner.name.capital} ${from.owner.name.conjugate(verb.singular)} ${prey.name.objective} ${preposition} ${to.consumePreposition} ${from.owner.pronouns.possessive} ${to.name}.`) } }