浏览代码

Add shrinking/growth; add Geta; check container capacity; make vore depend on size

master
Fen Dweller 5 年前
父节点
当前提交
4b76ea80f9
共有 10 个文件被更改,包括 232 次插入11 次删除
  1. +7
    -0
      src/game/combat.ts
  2. +7
    -7
      src/game/combat/actions.ts
  3. +10
    -0
      src/game/combat/conditions.ts
  4. +14
    -0
      src/game/combat/effects.ts
  5. +76
    -0
      src/game/combat/tests.ts
  6. +20
    -2
      src/game/creature.ts
  7. +2
    -1
      src/game/creatures.ts
  8. +77
    -0
      src/game/creatures/geta.ts
  9. +18
    -0
      src/game/maps/town.ts
  10. +1
    -1
      src/game/vore.ts

+ 7
- 0
src/game/combat.ts 查看文件

@@ -498,6 +498,13 @@ export class Effective {
log: nilLog log: nilLog
} }
} }

/**
* Changes a creature's size. This represents the change in *mass*
*/
scale (scale: number): number {
return scale
}
} }
/** /**
* A displayable status effect * A displayable status effect


+ 7
- 7
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 { DynText, LiveText, TextLike, Verb, PairLine, PairLineArgs } from '../language'
import { Entity } from '../entity' import { Entity } from '../entity'
import { Creature } from "../creature" import { Creature } from "../creature"
import { Damage, DamageFormula, Stat, Vigor, Action, Condition } from '../combat' import { Damage, DamageFormula, Stat, Vigor, Action, Condition } from '../combat'
import { LogLine, LogLines, LogEntry, nilLog } from '../interface' import { LogLine, LogLines, LogEntry, nilLog } from '../interface'
import { VoreContainer, Container } from '../vore' 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. * The PassAction has no effect.
@@ -85,15 +85,15 @@ export class AttackAction extends DamageAction {
* Devours the target. * Devours the target.
*/ */
export class DevourAction extends Action { export class DevourAction extends Action {
private test: StatVigorTest
private test: StatVigorSizeTest


constructor (protected container: Container) { constructor (protected container: Container) {
super( super(
new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'), 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 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 { allowed (user: Creature, target: Creature): boolean {
@@ -171,7 +171,7 @@ export class FeedAction extends Action {
* Tries to escape from the target's container * Tries to escape from the target's container
*/ */
export class StruggleAction extends Action { export class StruggleAction extends Action {
private test: StatVigorTest
private test: StatVigorSizeTest


constructor (public container: Container) { constructor (public container: Container) {
super( super(
@@ -179,7 +179,7 @@ export class StruggleAction extends Action {
'Try to escape from your foe', 'Try to escape from your foe',
[new CapableCondition(), new PairCondition(), new ContainedByCondition(container)] [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 { execute (user: Creature, target: Creature): LogEntry {


+ 10
- 0
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 { export class ContainedByCondition implements Condition {
constructor (private container: Container) { constructor (private container: Container) {




+ 14
- 0
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
}
}

+ 76
- 0
src/game/combat/tests.ts 查看文件

@@ -28,6 +28,82 @@ abstract class RandomTest implements CombatTest {
abstract explain(user: Creature, target: Creature): LogEntry 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 { export class StatVigorTest extends RandomTest {
private f: (x: number) => number private f: (x: number) => number
private k = 0.1 private k = 0.1


+ 20
- 2
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 { Noun, Pronoun } from './language'
import { LogEntry, LogLines } from './interface' import { LogEntry, LogLines } from './interface'
import { Vore, VoreContainer, VoreType } from './vore' import { Vore, VoreContainer, VoreType } from './vore'
@@ -33,10 +33,28 @@ export class Creature extends Vore implements Combatant {


this.actions.push(new PassAction()) this.actions.push(new PassAction())
this.side = Side.Heroes 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 { applyEffect (effect: StatusEffect): LogEntry {
this.effects.push(effect)
this.statusEffects.push(effect)
return effect.onApply(this) return effect.onApply(this)
} }




+ 2
- 1
src/game/creatures.ts 查看文件

@@ -8,5 +8,6 @@ import { Dragon } from './creatures/dragon'
import { Shingo } from './creatures/shingo' import { Shingo } from './creatures/shingo'
import { Goldeneye } from './creatures/goldeneye' import { Goldeneye } from './creatures/goldeneye'
import { Kuro } from './creatures/kuro' 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 }

+ 77
- 0
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)
)
]
}
)
)
}
}

+ 18
- 0
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( woods.choices.push(
new Choice( new Choice(
"Fight a dragon", "Fight a dragon",


+ 1
- 1
src/game/vore.ts 查看文件

@@ -52,7 +52,7 @@ export abstract class Vore extends Mortal {
0 0
) )
}, },
this.Mass
self.voreStats.Mass
) )
}, },
get [VoreStat.Mass] () { get [VoreStat.Mass] () {


正在加载...
取消
保存