From af0906561cbe8b61aa82e7054b8733805f6d1ba5 Mon Sep 17 00:00:00 2001 From: hypercross Date: Fri, 17 Apr 2026 15:30:28 +0800 Subject: [PATCH] fix: add effect triggering fixes --- .../system/combat/effects.ts | 29 ++++++++++- .../system/combat/triggers.ts | 49 +++++++++++-------- .../system/combat/types.ts | 4 +- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/samples/slay-the-spire-like/system/combat/effects.ts b/src/samples/slay-the-spire-like/system/combat/effects.ts index df01961..695318e 100644 --- a/src/samples/slay-the-spire-like/system/combat/effects.ts +++ b/src/samples/slay-the-spire-like/system/combat/effects.ts @@ -1,5 +1,11 @@ -import {CombatEntity, CombatState, EffectTable, PlayerEntity} from "./types"; -import {CardData, EffectData} from "@/samples/slay-the-spire-like/system/types"; +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"; @@ -80,6 +86,25 @@ export function* getAliveEnemies(state: CombatState) { } } +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); } diff --git a/src/samples/slay-the-spire-like/system/combat/triggers.ts b/src/samples/slay-the-spire-like/system/combat/triggers.ts index 46aa392..837fe9e 100644 --- a/src/samples/slay-the-spire-like/system/combat/triggers.ts +++ b/src/samples/slay-the-spire-like/system/combat/triggers.ts @@ -4,7 +4,7 @@ import { addItemEffect, getAliveEnemies, onEntityPostureDamage, onEntityEffectUpkeep, - onPlayerItemEffectUpkeep, onItemDiscard, onItemPlay, payCardCost + onPlayerItemEffectUpkeep, onItemDiscard, onItemPlay, payCardCost, getCombatEntity, getEffectTargets } from "@/samples/slay-the-spire-like/system/combat/effects"; import {promptMainAction} from "@/samples/slay-the-spire-like/system/combat/prompts"; import {moveToRegion, shuffle} from "@/core/region"; @@ -34,7 +34,7 @@ function createTriggers(){ onCombatStart: createTrigger("onCombatStart"), onTurnStart: createTrigger("onTurnStart", async ctx => { await ctx.game.produceAsync(draft => { - const entity = ctx.entityKey === "player" ? draft.player : draft.enemies.find(e => e.id === ctx.entityKey); + const entity = getCombatEntity(draft, ctx.entityKey); if(entity) onEntityEffectUpkeep(entity); if(entity === draft.player) onPlayerItemEffectUpkeep(draft.player); @@ -65,6 +65,13 @@ function createTriggers(){ moveToRegion(card, regions.hand, regions.discardPile); onItemPlay(draft.player, card.itemId); }); + const {cards, regions} = ctx.game.value.player.deck; + const card = cards[ctx.cardId]; + for(const [trigger, target, effect, stacks] of card.cardData.effects){ + if(trigger !== 'onPlay') continue; + for(const entity of getEffectTargets(target, ctx.game, ctx.targetId)) + await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId}); + } }), onCardDiscarded: createTrigger("onCardDiscarded", async ctx => { await ctx.game.produceAsync(draft => { @@ -72,12 +79,26 @@ function createTriggers(){ moveToRegion(cards[ctx.cardId], regions.hand, regions.discardPile); onItemDiscard(draft.player, cards[ctx.cardId].itemId); }); + const {cards, regions} = ctx.game.value.player.deck; + const card = cards[ctx.cardId]; + for(const [trigger, target, effect, stacks] of card.cardData.effects){ + if(trigger !== 'onDiscard') continue; + for(const entity of getEffectTargets(target, ctx.game)) + await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId}); + } }), onCardDrawn: createTrigger("onCardDrawn", async ctx => { await ctx.game.produceAsync(draft => { const {cards, regions} = draft.player.deck; moveToRegion(cards[ctx.cardId], regions.drawPile, regions.hand); }); + const {cards, regions} = ctx.game.value.player.deck; + const card = cards[ctx.cardId]; + for(const [trigger, target, effect, stacks] of card.cardData.effects){ + if(trigger !== 'onDraw') continue; + for(const entity of getEffectTargets(target, ctx.game)) + await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId}); + } }), onDraw: createTrigger("onDraw", async ctx => { let toDraw = ctx.count; @@ -138,26 +159,12 @@ function createTriggers(){ const enemy = ctx.game.value.enemies.find(e => e.id === ctx.enemyId); if(!enemy || !enemy.isAlive) return; - const intent = enemy.intents[enemy.currentIntentId]; + const intent = enemy.currentIntent; if(!intent) return; for(const [target, effect, stacks] of intent.effects){ - if(target === 'team'){ - for(const enemy of getAliveEnemies(ctx.game.value)){ - await triggers.onEffectApplied.execute(ctx.game, { - effect, - entityKey: enemy.id, - stacks, - }); - } - }else { - const entityKey = target === 'self' ? ctx.enemyId : 'player'; - await triggers.onEffectApplied.execute(ctx.game, { - effect, - entityKey, - stacks, - }); - } + for(const entity of getEffectTargets(target, ctx.game)) + await triggers.onEffectApplied.execute(ctx.game, { effect, entityKey: entity.id, stacks, }); } }), onIntentUpdate: createTrigger("onIntentUpdate", async ctx => { @@ -165,13 +172,13 @@ function createTriggers(){ const enemy = draft.enemies.find(e => e.id === ctx.enemyId); if(!enemy) return; - const intent = enemy.intents[enemy.currentIntentId]; + const intent = enemy.currentIntent; if(!intent) return; const nextIntents = intent.nextIntents; if(nextIntents.length > 0){ const nextIndex = ctx.game.rng.nextInt(nextIntents.length); - enemy.currentIntentId = nextIntents[nextIndex]; + enemy.currentIntent = nextIntents[nextIndex]; } }); }), diff --git a/src/samples/slay-the-spire-like/system/combat/types.ts b/src/samples/slay-the-spire-like/system/combat/types.ts index 7a0c874..25f1ef8 100644 --- a/src/samples/slay-the-spire-like/system/combat/types.ts +++ b/src/samples/slay-the-spire-like/system/combat/types.ts @@ -7,6 +7,7 @@ import {GameItemMeta} from "@/samples/slay-the-spire-like/system/progress"; export type EffectTable = Record; export type CombatEntity = { + id: string; // player is just "player" effects: EffectTable; hp: number; maxHp: number; @@ -21,10 +22,9 @@ export type PlayerEntity = CombatEntity & { } export type EnemyEntity = CombatEntity & { - id: string; enemy: EnemyData; intents: Record; - currentIntentId: string; + currentIntent: IntentData; }; export type CombatPhase = "playerTurn" | "enemyTurn" | "combatEnd";