import {createMiddlewareChain} from "../utils/middleware"; import {CombatGameContext} from "./types"; import {getAliveEnemies} from "@/samples/slay-the-spire-like/system/combat/utils"; import { onDiscard, onDraw, onEntityEffectUpkeep, onPlayerItemEffectUpkeep } from "@/samples/slay-the-spire-like/system/combat/effects"; import {promptMainAction} from "@/samples/slay-the-spire-like/system/combat/prompts"; type TriggerTypes = { onCombatStart: {}, onTurnStart: { entityKey: "player" | string, }, onTurnEnd: { entityKey: "player" | string, }, onShuffle: { entityKey: "player" | string, }, onCardPlayed: { cardId: string, targetId?: string }, onCardDiscarded: { cardId: string, }, onCardDrawn: { cardId: string, }, onEffectApplied: { effectId: string, entityKey: "player" | string, stacks: number, }, } function createTriggers(){ return { onCombatStart: createTrigger("onCombatStart"), onTurnStart: createTrigger("onTurnStart"), onTurnEnd: createTrigger("onTurnEnd"), onShuffle: createTrigger("onShuffle"), onCardPlayed: createTrigger("onCardPlayed"), onCardDiscarded: createTrigger("onCardDiscarded"), onCardDrawn: createTrigger("onCardDrawn"), onEffectApplied: createTrigger("onEffectApplied"), } } export type Triggers = ReturnType export function createStartWith(build: (triggers: Triggers) => void){ const triggers = createTriggers(); build(triggers); return async function(game: CombatGameContext){ await triggers.onCombatStart.execute(game,{}); // TODO at the end of a damage effect, if win/loss is achieved, break the loop with a throw // catch the throw and return the result here while(true){ await triggers.onTurnStart.execute(game,{entityKey: "player"}); await game.produceAsync(draft => { onEntityEffectUpkeep(draft.player); onPlayerItemEffectUpkeep(draft.player); }); while(true){ const action = await promptMainAction(game); if(action.action === "end-turn") break; if(action.action === "play"){ await game.produceAsync(draft => onDiscard(draft.player, action.cardId)); await triggers.onCardPlayed.execute(game, action); } } for(const cardId of [...game.value.player.deck.hand]){ await game.produceAsync(draft => onDiscard(draft.player, cardId)); await triggers.onCardDiscarded.execute(game,{cardId}); } await triggers.onTurnEnd.execute(game,{entityKey: "player"}); await game.produceAsync(draft => draft.player.energy = draft.player.maxEnergy); for(let i = 0; i < 5; i++){ const cardId = game.value.player.deck.drawPile[0]; // TODO: should this be drawPile[-1] ? if(!cardId) break; await game.produceAsync(draft => onDraw(draft.player, cardId)); await triggers.onCardDrawn.execute(game,{cardId}); } for(const enemy of getAliveEnemies(game.value)){ await triggers.onTurnStart.execute(game,{entityKey: enemy.id}); } await game.produceAsync(draft => { for(const enemy of getAliveEnemies(game.value)){ onEntityEffectUpkeep(enemy); } }); // TODO execute enemy intent, then update with new one here for(const enemy of getAliveEnemies(game.value)){ await triggers.onTurnEnd.execute(game,{entityKey: enemy.id}); } } } } function createTrigger(event: TKey) { type Ctx = TriggerTypes[TKey] & { event: TKey, game: CombatGameContext }; const {use, execute} = createMiddlewareChain(); return { use, execute: (game: CombatGameContext, ctx: TriggerTypes[TKey]) => execute({...ctx, event, game}), } }