import { CombatEntity, CombatGameContext, CombatState, EffectTable, PlayerEntity, } from "./types"; import { CardData, CardEffectTarget, CardTargetType, EffectData, EffectTarget, } from "@/samples/slay-the-spire-like/system/types"; import { GameItemMeta } from "@/samples/slay-the-spire-like/system/progress/types"; import { GridInventory } from "@/samples/slay-the-spire-like/system/grid-inventory/types"; export function addEffect( effects: EffectTable, effect: EffectData, stacks: number, ) { let current = effects[effect.id]; if (!current) current = { data: effect, stacks }; else current.stacks += stacks; if (current.stacks === 0 && effects[effect.id]) delete effects[effect.id]; else if (current.stacks !== 0 && !effects[effect.id]) effects[effect.id] = current; } export function addEntityEffect( entity: CombatEntity, effect: EffectData, stacks: number, ) { addEffect(entity.effects, effect, stacks); } export function addItemEffect( entity: PlayerEntity, itemKey: string, effect: EffectData, stacks: number, ) { entity.itemEffects[itemKey] = entity.itemEffects[itemKey] || {}; addEffect(entity.itemEffects[itemKey], effect, stacks); } export function onEntityEffectUpkeep(entity: CombatEntity) { for (const effect of Object.values(entity.effects)) { const lifecycle = effect.data.lifecycle; if (lifecycle === "temporary") addEntityEffect(entity, effect.data, -effect.stacks); else if (lifecycle === "lingering") addEntityEffect(entity, effect.data, effect.stacks >= 0 ? -1 : 1); } } export function onEntityPostureDamage(entity: CombatEntity, damage: number) { for (const effect of Object.values(entity.effects)) { const lifecycle = effect.data.lifecycle; if (lifecycle === "posture") addEntityEffect(entity, effect.data, -Math.min(damage, effect.stacks)); } } export function onPlayerItemEffectUpkeep(entity: PlayerEntity) { for (const [itemKey, itemEffects] of Object.entries(entity.itemEffects)) { for (const effect of Object.values(itemEffects)) { const lifecycle = effect.data.lifecycle; if (lifecycle === "itemTemporary") addItemEffect(entity, itemKey, effect.data, -effect.stacks); } } } export function onItemPlay(entity: PlayerEntity, itemKey: string) { const effects = entity.itemEffects[itemKey]; if (!effects) return; for (const effect of Object.values(effects)) { if (effect.data.lifecycle === "itemUntilPlay") { addItemEffect(entity, itemKey, effect.data, -effect.stacks); } } } export function onItemDiscard(entity: PlayerEntity, itemKey: string) { const effects = entity.itemEffects[itemKey]; if (!effects) return; for (const effect of Object.values(effects)) { if (effect.data.lifecycle === "itemUntilDiscard") { addItemEffect(entity, itemKey, effect.data, -effect.stacks); } } } export function* getAliveEnemies(state: CombatState) { for (let enemy of state.enemies) { if (enemy.isAlive) { yield enemy; } } } export function* getEffectTargets( target: CardEffectTarget | EffectTarget, game: CombatGameContext, targetId?: string, ) { if (target === "all" || target === "team") { for (const enemy of getAliveEnemies(game.value)) { yield enemy; } } else if (target === "self") { yield game.value.player; } else if (target === "target") { if (!targetId) return; const entity = getCombatEntity(game.value, targetId); if (entity) yield entity; } else if (target === "random") { const aliveEnemies = [...getAliveEnemies(game.value)]; if (aliveEnemies.length === 0) return; const index = game.rng.nextInt(aliveEnemies.length); yield aliveEnemies[index]; } } export function getCombatEntity(state: CombatState, entityKey: string) { return entityKey === "player" ? state.player : state.enemies.find((e) => e.id === entityKey); } export function canPlayCard( player: PlayerEntity, costType: CardData["costType"], costCount: number, itemId: string, inventory: GridInventory, ): boolean { if (costType === "energy") { return player.energy >= costCount; } if (costType === "uses") { const item = inventory.items.get(itemId); if (!item || !item.meta) return false; const depletion = item.meta.consumedUses ?? 0; return depletion < costCount; } return true; } export function payCardCost( player: PlayerEntity, costType: CardData["costType"], costCount: number, itemId: string, inventory: GridInventory, ): void { if (costType === "energy") { player.energy -= costCount; } else if (costType === "uses") { const item = inventory.items.get(itemId); if (item && item.meta) { item.meta.consumedUses = (item.meta.consumedUses ?? 0) + costCount; } } }