72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
import type { DungeonEncounter } from '../profile'
|
|
import { chooseRandom } from './rules'
|
|
|
|
export function encounterThreat(encounter: DungeonEncounter) {
|
|
return (
|
|
encounter.maxHealth
|
|
+ encounter.damage * 18
|
|
+ encounter.tankDamage * 10
|
|
+ encounter.partyDamage * 18
|
|
)
|
|
}
|
|
|
|
export function buildRoguelikeSegment<TMechanic extends string, TExtra extends object = object>({
|
|
pool,
|
|
stage,
|
|
mechanics,
|
|
trashCandidateCount,
|
|
bossCandidateCount,
|
|
healthScale,
|
|
damageScale,
|
|
partyDamageScale,
|
|
idBase,
|
|
bossDescription,
|
|
bossName,
|
|
fallbackBossFirst = false,
|
|
extraFields,
|
|
}: {
|
|
pool: DungeonEncounter[]
|
|
stage: number
|
|
mechanics: TMechanic[]
|
|
trashCandidateCount: (trashCount: number) => number
|
|
bossCandidateCount: (bossCount: number) => number
|
|
healthScale: number
|
|
damageScale: number
|
|
partyDamageScale: number
|
|
idBase: number
|
|
bossDescription: (mechanics: TMechanic[]) => string
|
|
bossName?: (encounter: DungeonEncounter) => string
|
|
fallbackBossFirst?: boolean
|
|
extraFields?: (encounter: DungeonEncounter, isBoss: boolean, mechanics: TMechanic[]) => TExtra
|
|
}) {
|
|
const trashPool = [...pool.filter((encounter) => !encounter.isBoss)]
|
|
.sort((left, right) => encounterThreat(left) - encounterThreat(right))
|
|
const bossPool = [...pool.filter((encounter) => encounter.isBoss)]
|
|
.sort((left, right) => encounterThreat(left) - encounterThreat(right))
|
|
const selectedTrash = chooseRandom(trashPool.slice(0, trashCandidateCount(trashPool.length)), 2)
|
|
const selectedBoss = chooseRandom(bossPool.slice(0, bossCandidateCount(bossPool.length)), 1)[0]
|
|
?? (fallbackBossFirst ? bossPool[0] : trashPool[0])
|
|
?? trashPool[0]
|
|
?? pool[0]
|
|
const selectedMechanics = mechanics
|
|
|
|
return [...selectedTrash, selectedBoss].map((encounter, index) => {
|
|
const isBoss = index === 2
|
|
return {
|
|
...encounter,
|
|
id: idBase + stage * 10 + index,
|
|
sequence: (stage - 1) * 3 + index + 1,
|
|
isBoss,
|
|
encounterType: isBoss ? 'boss' : 'trash',
|
|
enemyName: isBoss ? (bossName?.(encounter) ?? `${encounter.enemyName} ${stage}`) : encounter.enemyName,
|
|
description: isBoss ? bossDescription(selectedMechanics) : encounter.description,
|
|
maxHealth: Math.round(encounter.maxHealth * healthScale),
|
|
damage: Math.round(encounter.damage * damageScale),
|
|
tankDamage: Math.round(encounter.tankDamage * damageScale),
|
|
partyDamage: Math.round(encounter.partyDamage * partyDamageScale),
|
|
lootTables: [],
|
|
...(extraFields?.(encounter, isBoss, selectedMechanics) ?? {} as TExtra),
|
|
}
|
|
})
|
|
}
|