From 4b76ea80f9764673ef922fc8a24b1013985ee6a7 Mon Sep 17 00:00:00 2001 From: Fen Dweller Date: Fri, 7 Aug 2020 23:43:26 -0400 Subject: [PATCH] Add shrinking/growth; add Geta; check container capacity; make vore depend on size --- src/game/combat.ts | 7 ++++ src/game/combat/actions.ts | 14 +++---- src/game/combat/conditions.ts | 10 +++++ src/game/combat/effects.ts | 14 +++++++ src/game/combat/tests.ts | 76 ++++++++++++++++++++++++++++++++++ src/game/creature.ts | 22 +++++++++- src/game/creatures.ts | 3 +- src/game/creatures/geta.ts | 77 +++++++++++++++++++++++++++++++++++ src/game/maps/town.ts | 18 ++++++++ src/game/vore.ts | 2 +- 10 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 src/game/creatures/geta.ts diff --git a/src/game/combat.ts b/src/game/combat.ts index cf610bc..6f36748 100644 --- a/src/game/combat.ts +++ b/src/game/combat.ts @@ -498,6 +498,13 @@ export class Effective { log: nilLog } } + + /** + * Changes a creature's size. This represents the change in *mass* + */ + scale (scale: number): number { + return scale + } } /** * A displayable status effect diff --git a/src/game/combat/actions.ts b/src/game/combat/actions.ts index b60b8c7..7e94083 100644 --- a/src/game/combat/actions.ts +++ b/src/game/combat/actions.ts @@ -1,11 +1,11 @@ -import { StatTest, StatVigorTest } from './tests' +import { StatTest, StatVigorTest, StatVigorSizeTest } from './tests' import { DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '../language' import { Entity } from '../entity' import { Creature } from "../creature" import { Damage, DamageFormula, Stat, Vigor, Action, Condition } from '../combat' import { LogLine, LogLines, LogEntry, nilLog } from '../interface' import { VoreContainer, Container } from '../vore' -import { CapableCondition, UserDrainedVigorCondition, TogetherCondition, EnemyCondition, SoloCondition, PairCondition, ContainsCondition, ContainedByCondition } from './conditions' +import { CapableCondition, UserDrainedVigorCondition, TogetherCondition, EnemyCondition, SoloCondition, PairCondition, ContainsCondition, ContainedByCondition, HasRoomCondition } from './conditions' /** * The PassAction has no effect. @@ -85,15 +85,15 @@ export class AttackAction extends DamageAction { * Devours the target. */ export class DevourAction extends Action { - private test: StatVigorTest + private test: StatVigorSizeTest constructor (protected container: Container) { super( new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'), new LiveText(container, x => `Try to ${x.consumeVerb} your foe`), - [new CapableCondition(), new TogetherCondition()] + [new CapableCondition(), new TogetherCondition(), new HasRoomCondition(container)] ) - this.test = new StatVigorTest(Stat.Power) + this.test = new StatVigorSizeTest(Stat.Power) } allowed (user: Creature, target: Creature): boolean { @@ -171,7 +171,7 @@ export class FeedAction extends Action { * Tries to escape from the target's container */ export class StruggleAction extends Action { - private test: StatVigorTest + private test: StatVigorSizeTest constructor (public container: Container) { super( @@ -179,7 +179,7 @@ export class StruggleAction extends Action { 'Try to escape from your foe', [new CapableCondition(), new PairCondition(), new ContainedByCondition(container)] ) - this.test = new StatVigorTest(Stat.Power) + this.test = new StatVigorSizeTest(Stat.Power) } execute (user: Creature, target: Creature): LogEntry { diff --git a/src/game/combat/conditions.ts b/src/game/combat/conditions.ts index fcfd1eb..ac2427e 100644 --- a/src/game/combat/conditions.ts +++ b/src/game/combat/conditions.ts @@ -56,6 +56,16 @@ export class TogetherCondition implements Condition { } } +export class HasRoomCondition implements Condition { + constructor (private container: Container) { + + } + + allowed (user: Creature, target: Creature): boolean { + return this.container.capacity > this.container.fullness + target.voreStats.Bulk + } +} + export class ContainedByCondition implements Condition { constructor (private container: Container) { diff --git a/src/game/combat/effects.ts b/src/game/combat/effects.ts index 80cebd4..172e827 100644 --- a/src/game/combat/effects.ts +++ b/src/game/combat/effects.ts @@ -159,3 +159,17 @@ export class SurrenderEffect extends StatusEffect { } } } + +export class SizeEffect extends StatusEffect { + constructor (private change: number) { + super('Size-Shifted', 'This creature has changed in size', 'fas fa-ruler') + } + + onApply (creature: Creature): LogLine { + return new LogLine(`Smol`) + } + + scale (scale: number): number { + return scale * this.change + } +} diff --git a/src/game/combat/tests.ts b/src/game/combat/tests.ts index ea61989..44e6fa7 100644 --- a/src/game/combat/tests.ts +++ b/src/game/combat/tests.ts @@ -28,6 +28,82 @@ abstract class RandomTest implements CombatTest { abstract explain(user: Creature, target: Creature): LogEntry } +export class StatVigorSizeTest extends RandomTest { + private f: (x: number) => number + private k = 0.1 + + constructor (public readonly stat: Stat, private bias = 0) { + super() + this.f = logistic(0, 1, this.k) + } + + odds (user: Creature, target: Creature): number { + let userPercent = 1 + let targetPercent = 1 + + Object.keys(Vigor).forEach(key => { + userPercent *= user.vigors[key as Vigor] / Math.max(1, user.maxVigors[key as Vigor]) + targetPercent *= target.vigors[key as Vigor] / Math.max(1, target.maxVigors[key as Vigor]) + + userPercent = Math.max(0, userPercent) + targetPercent = Math.max(0, targetPercent) + }) + + if (userPercent === 0) { + targetPercent *= 4 + } + + if (targetPercent === 0) { + userPercent *= 4 + } + + userPercent *= Math.sqrt(user.voreStats.Mass) + targetPercent *= Math.sqrt(target.voreStats.Mass) + + return this.f(this.bias + user.stats[this.stat] * userPercent - target.stats[this.stat] * targetPercent) + } + + explain (user: Creature, target: Creature): LogEntry { + let result: LogEntry + + let userPercent = 1 + let targetPercent = 1 + + Object.keys(Vigor).forEach(key => { + userPercent *= user.vigors[key as Vigor] / user.maxVigors[key as Vigor] + targetPercent *= target.vigors[key as Vigor] / target.maxVigors[key as Vigor] + + userPercent = Math.max(0, userPercent) + targetPercent = Math.max(0, targetPercent) + }) + + if (userPercent === 0) { + targetPercent *= 4 + } + + if (targetPercent === 0) { + userPercent *= 4 + } + userPercent *= Math.sqrt(user.voreStats.Mass) + targetPercent *= Math.sqrt(target.voreStats.Mass) + const userMod = user.stats[this.stat] * userPercent + const targetMod = target.stats[this.stat] * targetPercent + const delta = userMod - targetMod + + if (delta === 0) { + result = new LogLine('You and the target have the same effective', new PropElem(this.stat), '.') + } else if (delta < 0) { + result = new LogLine('You effectively have ', new PropElem(this.stat, -delta), ' less than your foe.') + } else { + result = new LogLine('You effectively have ', new PropElem(this.stat, delta), ' more than you foe.') + } + + result = new LogLine(result, 'Your odds of success are ' + (100 * this.odds(user, target)).toFixed(1) + '%') + + return result + } +} + export class StatVigorTest extends RandomTest { private f: (x: number) => number private k = 0.1 diff --git a/src/game/creature.ts b/src/game/creature.ts index e1d07a5..5997616 100644 --- a/src/game/creature.ts +++ b/src/game/creature.ts @@ -1,4 +1,4 @@ -import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective } from './combat' +import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat } from './combat' import { Noun, Pronoun } from './language' import { LogEntry, LogLines } from './interface' import { Vore, VoreContainer, VoreType } from './vore' @@ -33,10 +33,28 @@ export class Creature extends Vore implements Combatant { this.actions.push(new PassAction()) this.side = Side.Heroes + + const baseVoreStats = this.voreStats + /* eslint-disable-next-line */ + const self = this + + this.voreStats = { + get [VoreStat.Bulk] () { + return baseVoreStats.Bulk + }, + get [VoreStat.Mass] () { + const base = baseVoreStats.Mass + const adjusted = self.effects.reduce((scale: number, effect: Effective) => effect.scale(scale), base) + return adjusted + }, + get [VoreStat.PreyCount] () { + return baseVoreStats["Prey Count"] + } + } } applyEffect (effect: StatusEffect): LogEntry { - this.effects.push(effect) + this.statusEffects.push(effect) return effect.onApply(this) } diff --git a/src/game/creatures.ts b/src/game/creatures.ts index 52d691b..6e4087c 100644 --- a/src/game/creatures.ts +++ b/src/game/creatures.ts @@ -8,5 +8,6 @@ import { Dragon } from './creatures/dragon' import { Shingo } from './creatures/shingo' import { Goldeneye } from './creatures/goldeneye' import { Kuro } from './creatures/kuro' +import { Geta } from './creatures/geta' -export { Wolf, Player, Cafat, Human, Withers, Kenzie, Dragon, Shingo, Goldeneye, Kuro } +export { Wolf, Player, Cafat, Human, Withers, Kenzie, Dragon, Shingo, Goldeneye, Kuro, Geta } diff --git a/src/game/creatures/geta.ts b/src/game/creatures/geta.ts new file mode 100644 index 0000000..80a0beb --- /dev/null +++ b/src/game/creatures/geta.ts @@ -0,0 +1,77 @@ +import { Creature } from "../creature" +import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, CompositionAction } from '../combat' +import { MalePronouns, ImproperNoun, ProperNoun, ObjectPronouns, FemalePronouns, TheyPronouns } from '../language' +import { VoreType, Stomach, Bowels, Cock, Balls, anyVore, Slit, Womb, biconnectContainers } from '../vore' +import { AttackAction, TransferAction, FeedAction } from '../combat/actions' +import { StatusConsequence, LogConsequence, DamageConsequence } from '../combat/consequences' +import { SizeEffect, DamageTypeResistanceEffect } from '../combat/effects' +import { LogLine } from '../interface' + +export class Geta extends Creature { + constructor () { + super( + new ProperNoun('Geta'), + new ImproperNoun('fox', 'foxes'), + MalePronouns, + { Toughness: 10, Power: 10, Speed: 30, Willpower: 15, Charm: 40 }, + new Set([VoreType.Oral, VoreType.Anal, VoreType.Cock]), + new Set([VoreType.Oral, VoreType.Anal, VoreType.Cock]), + 100 + ) + + this.side = Side.Monsters + + const stomach = new Stomach(this, 10, new Damage( + { amount: 100, type: DamageType.Acid, target: Vigor.Health }, + { amount: 40, type: DamageType.Crush, target: Vigor.Stamina }, + { amount: 80, type: DamageType.Dominance, target: Vigor.Resolve } + )) + this.containers.push(stomach) + + const bowels = new Bowels(this, 10, new Damage( + { amount: 30, type: DamageType.Crush, target: Vigor.Health }, + { amount: 90, type: DamageType.Crush, target: Vigor.Stamina }, + { amount: 120, type: DamageType.Dominance, target: Vigor.Resolve } + )) + + this.containers.push(bowels) + + this.actions.push(new TransferAction(bowels, stomach)) + + this.otherActions.push(new FeedAction(stomach)) + + const cock = new Cock(this, 5, new Damage( + { amount: 10, type: DamageType.Crush, target: Vigor.Health }, + { amount: 30, type: DamageType.Crush, target: Vigor.Stamina }, + { amount: 30, type: DamageType.Dominance, target: Vigor.Resolve } + )) + + const balls = new Balls(this, 10, new Damage( + { amount: 50, type: DamageType.Acid, target: Vigor.Health }, + { amount: 25, type: DamageType.Crush, target: Vigor.Stamina }, + { amount: 150, type: DamageType.Dominance, target: Vigor.Resolve } + ), cock) + + this.containers.push(balls) + this.containers.push(cock) + + biconnectContainers(cock, balls) + + this.actions.push( + new CompositionAction( + "Shrink", + "Zap!", + { + consequences: [ + new LogConsequence( + (user, target) => new LogLine(`ZAP!`) + ), + new StatusConsequence( + () => new SizeEffect(0.25) + ) + ] + } + ) + ) + } +} diff --git a/src/game/maps/town.ts b/src/game/maps/town.ts index 77f0a90..8061ab5 100644 --- a/src/game/maps/town.ts +++ b/src/game/maps/town.ts @@ -113,6 +113,24 @@ export const Town = (): Place => { ) ) + woods.choices.push( + new Choice( + "Fight Geta", + "yolo", + (world, executor) => { + world.encounter = new Encounter( + { + name: "You punched Geta", + intro: (world: World) => new LogLine(`You punched Geta. Geta is angry.`) + }, + [executor, new Creatures.Geta()] + ) + + return new LogLine(`FIGHT TIME`) + } + ) + ) + woods.choices.push( new Choice( "Fight a dragon", diff --git a/src/game/vore.ts b/src/game/vore.ts index be68862..63c1074 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -52,7 +52,7 @@ export abstract class Vore extends Mortal { 0 ) }, - this.Mass + self.voreStats.Mass ) }, get [VoreStat.Mass] () {