feat: more effects

This commit is contained in:
hypercross 2026-04-17 17:15:31 +08:00
parent 131af2c0bb
commit 90ca8fa761
2 changed files with 89 additions and 2 deletions

View File

@ -7,6 +7,8 @@ import { moveToRegion } from "@/core/region";
import { CombatGameContext } from "@/samples/slay-the-spire-like/system/combat/types"; import { CombatGameContext } from "@/samples/slay-the-spire-like/system/combat/types";
import { EffectData } from "@/samples/slay-the-spire-like/system/types"; import { EffectData } from "@/samples/slay-the-spire-like/system/types";
import { GameCard } from "@/samples/slay-the-spire-like/system/deck"; 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) { export function addEffectTriggers(triggers: Triggers) {
// ========== instant effects ========== // ========== instant effects ==========
@ -430,6 +432,91 @@ export function addEffectTriggers(triggers: Triggers) {
await next(); 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<GameItemMeta>(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) { function getAllEnemyData(game: CombatGameContext) {

View File

@ -22,7 +22,7 @@ type TriggerTypes = {
onCardDiscarded: { cardId: string, sourceEntityKey?: "player" | string }, onCardDiscarded: { cardId: string, sourceEntityKey?: "player" | string },
onCardDrawn: { cardId: string, sourceEntityKey?: "player" | string }, onCardDrawn: { cardId: string, sourceEntityKey?: "player" | string },
onDraw: {count: number}, 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}, onHpChange: { entityKey: "player" | string, amount: number},
onDamage: { entityKey: "player" | string, amount: number, prevented?: number, sourceEntityKey?: "player" | string}, onDamage: { entityKey: "player" | string, amount: number, prevented?: number, sourceEntityKey?: "player" | string},
onEnemyIntent: { enemyId: string, 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){ for(const [trigger, target, effect, stacks] of card.cardData.effects){
if(trigger !== 'onPlay') continue; if(trigger !== 'onPlay') continue;
for(const entity of getEffectTargets(target, ctx.game, ctx.targetId)) 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 => { onCardDiscarded: createTrigger("onCardDiscarded", async ctx => {