From 90ca8fa7619cdc6c1d3f9e12ae49d953a7e0decf Mon Sep 17 00:00:00 2001 From: hypercross Date: Fri, 17 Apr 2026 17:15:31 +0800 Subject: [PATCH] feat: more effects --- .../data/desert/triggers/effect.ts | 87 +++++++++++++++++++ .../system/combat/triggers.ts | 4 +- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/samples/slay-the-spire-like/data/desert/triggers/effect.ts b/src/samples/slay-the-spire-like/data/desert/triggers/effect.ts index ce493ff..b81d286 100644 --- a/src/samples/slay-the-spire-like/data/desert/triggers/effect.ts +++ b/src/samples/slay-the-spire-like/data/desert/triggers/effect.ts @@ -7,6 +7,8 @@ import { moveToRegion } from "@/core/region"; import { CombatGameContext } from "@/samples/slay-the-spire-like/system/combat/types"; import { EffectData } from "@/samples/slay-the-spire-like/system/types"; import { GameCard } from "@/samples/slay-the-spire-like/system/deck"; +import { getAdjacentItems } from "@/samples/slay-the-spire-like/system/grid-inventory"; +import { GameItemMeta } from "@/samples/slay-the-spire-like/system/progress"; export function addEffectTriggers(triggers: Triggers) { // ========== instant effects ========== @@ -430,6 +432,91 @@ export function addEffectTriggers(triggers: Triggers) { await next(); }); + + // ========== crossbow: replay other crossbows on same target ========== + triggers.onEffectApplied.use(async (ctx, next) => { + await next(); + + if (ctx.effect.id !== "crossbow" || !ctx.cardId || !ctx.targetId) return; + + const { cards, regions } = ctx.game.value.player.deck; + const handIds = [...regions.hand.childIds]; + for (const id of handIds) { + const card = cards[id]; + if (card && card.itemId === "crossbow" && id !== ctx.cardId) { + await triggers.onCardPlayed.execute(ctx.game, { + cardId: id, + targetId: ctx.targetId, + sourceEntityKey: "player", + }); + } + } + }); + + // ========== burnForEnergy: consume adjacent item, gain energy when its card is played ========== + triggers.onCardPlayed.use(async (ctx, next) => { + await next(); + + const card = ctx.game.value.player.deck.cards[ctx.cardId]; + if (!card) return; + const playedItemId = card.itemId; + + const adjacent = getAdjacentItems(ctx.game.value.inventory, playedItemId); + for (const [adjItemId] of adjacent) { + const adjEffects = ctx.game.value.player.itemEffects[adjItemId]; + if (!adjEffects) continue; + const burn = adjEffects.burnForEnergy; + if (!burn || burn.stacks <= 0) continue; + + await ctx.game.produceAsync(draft => { + const item = draft.inventory.items.get(adjItemId); + if (item) { + draft.inventory.items.delete(adjItemId); + } + draft.player.energy += burn.stacks; + delete draft.player.itemEffects[adjItemId]; + }); + break; + } + }); + + // ========== sandwormKing: heal 10 hp when player discards fatigue ========== + triggers.onCardDiscarded.use(async (ctx, next) => { + await next(); + + const card = ctx.game.value.player.deck.cards[ctx.cardId]; + if (!card || card.cardData.id !== "fatigue") return; + + const sandwormKing = ctx.game.value.enemies.find( + e => e.enemy.id === "沙虫王" && e.isAlive + ); + if (!sandwormKing) return; + + await ctx.game.produceAsync(draft => { + const king = draft.enemies.find(e => e.id === sandwormKing.id); + if (king) { + king.hp = Math.min(king.hp + 10, king.maxHp); + } + }); + }); + + // ========== vulture: give vultureEye when vulture deals damage ========== + triggers.onDamage.use(async (ctx, next) => { + await next(); + + const dealt = ctx.amount - (ctx.prevented ?? 0); + if (dealt <= 0 || !ctx.sourceEntityKey) return; + + const attacker = getCombatEntity(ctx.game.value, ctx.sourceEntityKey); + if (!attacker || !("enemy" in attacker) || attacker.enemy.id !== "秃鹫") return; + + await triggers.onEffectApplied.execute(ctx.game, { + effect: findEffect(ctx.game, "vultureEye"), + entityKey: "player", + stacks: 1, + sourceEntityKey: ctx.sourceEntityKey, + }); + }); } function getAllEnemyData(game: CombatGameContext) { 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 60feab7..dac0304 100644 --- a/src/samples/slay-the-spire-like/system/combat/triggers.ts +++ b/src/samples/slay-the-spire-like/system/combat/triggers.ts @@ -22,7 +22,7 @@ type TriggerTypes = { onCardDiscarded: { cardId: string, sourceEntityKey?: "player" | string }, onCardDrawn: { cardId: string, sourceEntityKey?: "player" | string }, onDraw: {count: number}, - onEffectApplied: { effect: EffectData, entityKey: "player" | string, stacks: number, cardId?: string, sourceEntityKey?: "player" | string }, + onEffectApplied: { effect: EffectData, entityKey: "player" | string, stacks: number, cardId?: string, sourceEntityKey?: "player" | string, targetId?: string }, onHpChange: { entityKey: "player" | string, amount: number}, onDamage: { entityKey: "player" | string, amount: number, prevented?: number, sourceEntityKey?: "player" | string}, onEnemyIntent: { enemyId: string, sourceEntityKey?: "player" | string }, @@ -71,7 +71,7 @@ function createTriggers(){ 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, sourceEntityKey: source}); + await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId, sourceEntityKey: source, targetId: ctx.targetId}); } }), onCardDiscarded: createTrigger("onCardDiscarded", async ctx => {