Kaynağa Gözat

Fix display of opposed stat tests; change some actions to use it

vintage
Fen Dweller 5 yıl önce
ebeveyn
işleme
c77136aabd
4 değiştirilmiş dosya ile 68 ekleme ve 27 silme
  1. +16
    -10
      src/game/combat/actions.ts
  2. +17
    -0
      src/game/combat/perks.ts
  3. +32
    -16
      src/game/combat/tests.ts
  4. +3
    -1
      src/game/maps/town.ts

+ 16
- 10
src/game/combat/actions.ts Dosyayı Görüntüle

@@ -1,4 +1,4 @@
import { StatTest, StatVigorTest, StatVigorSizeTest } from './tests'
import { StatTest, StatVigorTest, StatVigorSizeTest, OpposedStatTest, TestCategory } 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"
@@ -98,10 +98,12 @@ export class DevourAction extends CompositionAction {
new LiveText(container, x => `Try to ${x.consumeVerb} your foe`), new LiveText(container, x => `Try to ${x.consumeVerb} your foe`),
{ {
conditions: [new CapableCondition(), new TogetherCondition(), new HasRoomCondition(container)], conditions: [new CapableCondition(), new TogetherCondition(), new HasRoomCondition(container)],
tests: [new StatVigorSizeTest(
Stat.Power,
-5,
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${container.consumeVerb} ${target.name.objective}.`)
tests: [new OpposedStatTest(
{ Power: 1, Charm: 1, Mass: 0.05 },
{ Toughness: 1, Willpower: 1, Bulk: 0.05 },
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${container.consumeVerb} ${target.name.objective}.`),
TestCategory.Vore,
-5
)], )],
consequences: [ consequences: [
new ConsumeConsequence(container) new ConsumeConsequence(container)
@@ -178,11 +180,15 @@ export class StruggleAction extends Action {
new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'), new DynText('Struggle (', new LiveText(container, x => x.name.all), ')'),
'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)],
[new StatVigorSizeTest(
Stat.Power,
-5,
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to escape from ${target.name.possessive} ${container.name}.`)
)]
[
new OpposedStatTest(
{ Power: 1, Agility: 1, Bulk: 0.05 },
{ Toughness: 1, Reflexes: 1, Mass: 0.05 },
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to escape from ${target.name.possessive} ${container.name}.`),
TestCategory.Vore,
-5
)
]
) )
} }




+ 17
- 0
src/game/combat/perks.ts Dosyayı Görüntüle

@@ -55,3 +55,20 @@ export class RavenousPerk extends Perk {
} }
} }
} }

export class DeliciousPerk extends Perk {
constructor () {
super(
"Delicious",
"-20 to all defensive vore checks"
)
}

modTestDefense (defender: Creature, attacker: Creature, kind: TestCategory): number {
if (kind === TestCategory.Vore) {
return -20
} else {
return 0
}
}
}

+ 32
- 16
src/game/combat/tests.ts Dosyayı Görüntüle

@@ -1,4 +1,4 @@
import { CombatTest, Stat, Vigor, Stats, StatToVigor } from '../combat'
import { CombatTest, Stat, Vigor, Stats, StatToVigor, VoreStats, VoreStat } from '../combat'
import { Creature } from "../creature" import { Creature } from "../creature"
import { LogEntry, LogLines, PropElem, LogLine, nilLog } from '../interface' import { LogEntry, LogLines, PropElem, LogLine, nilLog } from '../interface'
import { Verb } from '../language' import { Verb } from '../language'
@@ -49,8 +49,8 @@ export class OpposedStatTest extends RandomTest {
private maxTotalVigorPenalty = 0.1 private maxTotalVigorPenalty = 0.1


constructor ( constructor (
public readonly userStats: Partial<Stats>,
public readonly targetStats: Partial<Stats>,
public readonly userStats: Partial<Stats & VoreStats>,
public readonly targetStats: Partial<Stats & VoreStats>,
fail: (user: Creature, target: Creature) => LogEntry, fail: (user: Creature, target: Creature) => LogEntry,
public category: TestCategory, public category: TestCategory,
private bias = 0 private bias = 0
@@ -60,8 +60,8 @@ export class OpposedStatTest extends RandomTest {
} }


odds (user: Creature, target: Creature): number { odds (user: Creature, target: Creature): number {
const userScore = this.getScore(user, this.userStats) + user.effects.reduce((total, effect) => total + effect.modTestOffense(user, target, this.category), 0)
const targetScore = this.getScore(target, this.targetStats) + target.effects.reduce((total, effect) => total + effect.modTestDefense(target, user, this.category), 0)
const userScore = this.getScoreOffense(user, target, this.userStats)
const targetScore = this.getScoreDefense(target, user, this.targetStats)


return this.f(userScore - targetScore + this.bias) return this.f(userScore - targetScore + this.bias)
} }
@@ -72,23 +72,22 @@ export class OpposedStatTest extends RandomTest {
`Pits `, `Pits `,
...Object.entries(this.userStats).map(([stat, frac]) => { ...Object.entries(this.userStats).map(([stat, frac]) => {
if (frac !== undefined) { if (frac !== undefined) {
return new LogLine(`${(frac * 100).toFixed(0)}% `, new PropElem(stat as Stat))
return new LogLine(`${(frac * 100).toFixed(0)}% of `, new PropElem(stat as Stat), `, `)
} else { } else {
return nilLog return nilLog
} }
}), }),
` from ${user.name.possessive} stats against `,
` against `,
...Object.entries(this.targetStats).map(([stat, frac]) => { ...Object.entries(this.targetStats).map(([stat, frac]) => {
if (frac !== undefined) { if (frac !== undefined) {
return new LogLine(`${(frac * 100).toFixed(0)}% `, new PropElem(stat as Stat))
return new LogLine(`${(frac * 100).toFixed(0)}% of `, new PropElem(stat as Stat), `, `)
} else { } else {
return nilLog return nilLog
} }
}),
` from ${target.name.possessive} stats.`
})
), ),
new LogLine( new LogLine(
`${user.name.capital}: ${this.getScore(user, this.userStats)} // ${this.getScore(target, this.targetStats)} :${target.name.capital}`
`${user.name.capital}: ${this.getScoreOffense(user, target, this.userStats)} // ${this.getScoreDefense(target, user, this.targetStats)} :${target.name.capital}`
), ),
new LogLine( new LogLine(
`${user.name.capital} ${user.name.conjugate(new Verb("have", "has"))} a ${(this.odds(user, target) * 100).toFixed(0)}% chance of winning this test.` `${user.name.capital} ${user.name.conjugate(new Verb("have", "has"))} a ${(this.odds(user, target) * 100).toFixed(0)}% chance of winning this test.`
@@ -96,22 +95,39 @@ export class OpposedStatTest extends RandomTest {
) )
} }


private getScoreDefense (defender: Creature, attacker: Creature, parts: Partial<Stats>): number {
return this.getScore(defender, parts) + defender.effects.reduce((total, effect) => total + effect.modTestDefense(defender, attacker, this.category), 0)
}

private getScoreOffense (attacker: Creature, defender: Creature, parts: Partial<Stats>): number {
return this.getScore(attacker, parts) + attacker.effects.reduce((total, effect) => total + effect.modTestOffense(attacker, defender, this.category), 0)
}

private getScore (actor: Creature, parts: Partial<Stats>): number { private getScore (actor: Creature, parts: Partial<Stats>): number {
const total = Object.entries(parts).reduce((total: number, [stat, frac]) => { const total = Object.entries(parts).reduce((total: number, [stat, frac]) => {
let value = actor.stats[stat as Stat] * (frac === undefined ? 0 : frac)
if (stat in Stat) {
let value = actor.stats[stat as Stat] * (frac === undefined ? 0 : frac)

const vigor = StatToVigor[stat as Stat]
value = value * (1 - this.maxStatVigorPenalty) + value * this.maxStatVigorPenalty * actor.vigors[vigor] / actor.maxVigors[vigor]


const vigor = StatToVigor[stat as Stat]
value = value * (1 - this.maxStatVigorPenalty) + value * this.maxStatVigorPenalty * actor.vigors[vigor] / actor.maxVigors[vigor]
return total + value
} else if (stat in VoreStat) {
const value = actor.voreStats[stat as VoreStat] * (frac === undefined ? 0 : frac)


return total + value
return total + value
} else {
return total
}
}, 0) }, 0)


const modifiedTotal = Object.keys(Vigor).reduce( const modifiedTotal = Object.keys(Vigor).reduce(
(total, vigor) => { (total, vigor) => {
return total * (1 - this.maxStatVigorPenalty) + total * actor.vigors[vigor as Vigor] / actor.maxVigors[vigor as Vigor]
return total * (1 - this.maxStatVigorPenalty) + total * this.maxStatVigorPenalty * actor.vigors[vigor as Vigor] / actor.maxVigors[vigor as Vigor]
}, },
total total
) )

return modifiedTotal return modifiedTotal
} }
} }


+ 3
- 1
src/game/maps/town.ts Dosyayı Görüntüle

@@ -9,6 +9,7 @@ import { DevourAction } from '../combat/actions'
import { SurrenderEffect } from '../combat/effects' import { SurrenderEffect } from '../combat/effects'
import moment from 'moment' import moment from 'moment'
import { VoreAI } from '../ai' import { VoreAI } from '../ai'
import { DeliciousPerk } from '../combat/perks'


function makeParty (): Creature[] { function makeParty (): Creature[] {
const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, {
@@ -265,9 +266,10 @@ export const Town = (): Place => {
enemy.side = Side.Monsters enemy.side = Side.Monsters
enemy.ai = new VoreAI() enemy.ai = new VoreAI()
enemy.equip(new Items.Sword(), Items.EquipmentSlot.MainHand) enemy.equip(new Items.Sword(), Items.EquipmentSlot.MainHand)
enemy.perks.push(new DeliciousPerk())
const encounter = new Encounter( const encounter = new Encounter(
{ {
name: "Fight some nerd",
name: "Fight some tasty nerd",
intro: world => new LogLine(`You find some nerd to fight.`) intro: world => new LogLine(`You find some nerd to fight.`)
}, },
[world.player, enemy] [world.player, enemy]


Yükleniyor…
İptal
Kaydet