fix: add effect triggering fixes

This commit is contained in:
hypercross 2026-04-17 15:30:28 +08:00
parent aedf82d264
commit af0906561c
3 changed files with 57 additions and 25 deletions

View File

@ -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);
}

View File

@ -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];
}
});
}),

View File

@ -7,6 +7,7 @@ import {GameItemMeta} from "@/samples/slay-the-spire-like/system/progress";
export type EffectTable = Record<string, {data: EffectData, stacks: number}>;
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<string, IntentData>;
currentIntentId: string;
currentIntent: IntentData;
};
export type CombatPhase = "playerTurn" | "enemyTurn" | "combatEnd";