feat: cost implementation for card play

This commit is contained in:
2026-04-17 14:10:28 +08:00
parent 8155747cac
commit fb66ec55c4
4 changed files with 174 additions and 10 deletions
@@ -1,7 +1,7 @@
import {CombatEntity, EffectTable} from "./types";
import {EffectData} from "@/samples/slay-the-spire-like/system/types";
import {PlayerEntity} from "@/samples/slay-the-spire-like/system/combat/types";
import {CombatState} from "./types";
import {CombatEntity, CombatState, EffectTable, PlayerEntity} from "./types";
import {CardData, EffectData} 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";
export function addEffect(effects: EffectTable, effect: EffectData, stacks: number){
let current = effects[effect.id];
@@ -82,4 +82,28 @@ export function* getAliveEnemies(state: CombatState) {
export function getCombatEntity(state: CombatState, entityKey: string){
return entityKey === 'player' ? state.player : state.enemies.find(e => e.id === entityKey);
}
}
export function canPlayCard(player: PlayerEntity, costType: CardData['costType'], costCount: number, itemId: string, inventory: GridInventory<GameItemMeta>): boolean {
if (costType === 'energy') {
return player.energy >= costCount;
}
if (costType === 'uses') {
const item = inventory.items.get(itemId);
if (!item || !item.meta) return false;
const depletion = item.meta.depletion ?? 0;
return depletion < costCount;
}
return true;
}
export function payCardCost(player: PlayerEntity, costType: CardData['costType'], costCount: number, itemId: string, inventory: GridInventory<GameItemMeta>): void {
if (costType === 'energy') {
player.energy -= costCount;
} else if (costType === 'uses') {
const item = inventory.items.get(itemId);
if (item && item.meta) {
item.meta.depletion = (item.meta.depletion ?? 0) + costCount;
}
}
}
@@ -1,5 +1,6 @@
import { createPromptDef } from "@/core/game";
import {CombatGameContext} from "./types";
import {canPlayCard} from "@/samples/slay-the-spire-like/system/combat/effects";
export const prompts = {
mainAction: createPromptDef<[string, string?]>(
@@ -18,7 +19,12 @@ export async function promptMainAction(game: CombatGameContext){
if(!exists) throw `卡牌"${cardId}"不在手牌中`;
const card = game.value.player.deck.cards[cardId];
const {targetType} = card.cardData;
const {cardData, itemId} = card;
if(!canPlayCard(game.value.player, cardData.costType, cardData.costCount, itemId, game.value.inventory)){
throw `无法支付卡牌"${cardId}"的费用`;
}
const {targetType} = cardData;
if(targetType === 'single'){
if(!targetId) throw `请指定目标`;
const target = game.value.enemies.find(e => e.id === targetId);
@@ -4,7 +4,7 @@ import {
addItemEffect,
getAliveEnemies, onEntityPostureDamage,
onEntityEffectUpkeep,
onPlayerItemEffectUpkeep, onItemDiscard, onItemPlay
onPlayerItemEffectUpkeep, onItemDiscard, onItemPlay, payCardCost
} 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";
@@ -60,8 +60,10 @@ function createTriggers(){
onCardPlayed: createTrigger("onCardPlayed", async ctx => {
await ctx.game.produceAsync(draft => {
const {cards, regions} = draft.player.deck;
moveToRegion(cards[ctx.cardId], regions.hand, regions.discardPile);
onItemPlay(draft.player, cards[ctx.cardId].itemId);
const card = cards[ctx.cardId];
payCardCost(draft.player, card.cardData.costType, card.cardData.costCount, card.itemId, draft.inventory);
moveToRegion(card, regions.hand, regions.discardPile);
onItemPlay(draft.player, card.itemId);
});
}),
onCardDiscarded: createTrigger("onCardDiscarded", async ctx => {
@@ -190,7 +192,6 @@ export function createStartWith(build: (triggers: Triggers) => void){
const action = await promptMainAction(game);
if (action.action === "end-turn") break;
if (action.action === "play") {
// TODO: energy/use consumption handling
await triggers.onCardPlayed.execute(game, action);
}
}