Compare commits
3 Commits
a80852bc59
...
131af2c0bb
| Author | SHA1 | Date |
|---|---|---|
|
|
131af2c0bb | |
|
|
02c159f8ae | |
|
|
e09e24e274 |
|
|
@ -1,5 +0,0 @@
|
|||
declare module '*.yarnproject' {
|
||||
import type { LoadResult } from 'yarn-spinner-loader';
|
||||
const result: LoadResult;
|
||||
export default result;
|
||||
}
|
||||
3
src/samples/slay-the-spire-like/data/desert/dialogues/dialogues.yarnproject.d.ts
vendored
Normal file
3
src/samples/slay-the-spire-like/data/desert/dialogues/dialogues.yarnproject.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import type { LoadResult } from 'yarn-spinner-loader';
|
||||
const result: LoadResult;
|
||||
export default result;
|
||||
|
|
@ -1,33 +1,455 @@
|
|||
import {Triggers} from "@/samples/slay-the-spire-like/system/combat/triggers";
|
||||
import {getCombatEntity} from "@/samples/slay-the-spire-like/system/combat/effects";
|
||||
import { Triggers } from "@/samples/slay-the-spire-like/system/combat/triggers";
|
||||
import {
|
||||
addEntityEffect,
|
||||
getCombatEntity,
|
||||
} from "@/samples/slay-the-spire-like/system/combat/effects";
|
||||
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";
|
||||
|
||||
export function addEffectTriggers(triggers: Triggers) {
|
||||
// instant effects
|
||||
// ========== instant effects ==========
|
||||
triggers.onEffectApplied.use(async (ctx, next) => {
|
||||
if (ctx.effect.id === "attack") {
|
||||
await triggers.onDamage.execute(ctx.game, {
|
||||
entityKey: ctx.entityKey,
|
||||
amount: ctx.stacks
|
||||
amount: ctx.stacks,
|
||||
sourceEntityKey: ctx.sourceEntityKey ?? ctx.entityKey === "player" ? undefined : "player",
|
||||
});
|
||||
} else if (ctx.effect.id === "draw") {
|
||||
await triggers.onDraw.execute(ctx.game, {
|
||||
count: ctx.stacks
|
||||
await triggers.onDraw.execute(ctx.game, { count: ctx.stacks });
|
||||
} else if (ctx.effect.id === "gainEnergy") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
draft.player.energy += ctx.stacks;
|
||||
});
|
||||
} else if (ctx.effect.id === "removeWound") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const { cards, regions } = draft.player.deck;
|
||||
let removed = 0;
|
||||
const allPileIds = [
|
||||
...regions.drawPile.childIds,
|
||||
...regions.discardPile.childIds,
|
||||
];
|
||||
for (const cardId of allPileIds) {
|
||||
if (removed >= ctx.stacks) break;
|
||||
const card = cards[cardId];
|
||||
if (card && card.cardData.id === "wound") {
|
||||
const sourceRegion = card.regionId === "drawPile" ? regions.drawPile : regions.discardPile;
|
||||
moveToRegion(card, sourceRegion, null);
|
||||
delete cards[cardId];
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (ctx.effect.id === "venom") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const cardId = `venom-${draft.player.deck.regions.drawPile.childIds.length}-${draft.player.deck.regions.discardPile.childIds.length}`;
|
||||
const card: GameCard = {
|
||||
id: cardId,
|
||||
regionId: "",
|
||||
position: [],
|
||||
itemId: "venom",
|
||||
cardData: {
|
||||
id: "venom",
|
||||
name: "蛇毒",
|
||||
desc: "弃掉时受到3点伤害",
|
||||
type: "status",
|
||||
costType: "energy",
|
||||
costCount: 1,
|
||||
targetType: "none",
|
||||
effects: [["onDiscard", "self", ctx.effect, 3]],
|
||||
},
|
||||
};
|
||||
draft.player.deck.cards[cardId] = card;
|
||||
moveToRegion(card, null, draft.player.deck.regions.drawPile);
|
||||
});
|
||||
} else if (ctx.effect.id === "vultureEye") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const cardId = `vultureEye-${draft.player.deck.regions.drawPile.childIds.length}-${draft.player.deck.regions.discardPile.childIds.length}`;
|
||||
const card: GameCard = {
|
||||
id: cardId,
|
||||
regionId: "",
|
||||
position: [],
|
||||
itemId: "vultureEye",
|
||||
cardData: {
|
||||
id: "vultureEye",
|
||||
name: "秃鹫之眼",
|
||||
desc: "抓到时获得3层暴露",
|
||||
type: "status",
|
||||
costType: "none",
|
||||
costCount: 0,
|
||||
targetType: "none",
|
||||
effects: [["onDraw", "self", ctx.effect, 3]],
|
||||
},
|
||||
};
|
||||
draft.player.deck.cards[cardId] = card;
|
||||
moveToRegion(card, null, draft.player.deck.regions.drawPile);
|
||||
});
|
||||
} else if (ctx.effect.id === "static") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const cardId = `static-${draft.player.deck.regions.drawPile.childIds.length}-${draft.player.deck.regions.discardPile.childIds.length}`;
|
||||
const card: GameCard = {
|
||||
id: cardId,
|
||||
regionId: "",
|
||||
position: [],
|
||||
itemId: "static",
|
||||
cardData: {
|
||||
id: "static",
|
||||
name: "静电",
|
||||
desc: "在手里时受电击伤害+1",
|
||||
type: "status",
|
||||
costType: "none",
|
||||
costCount: 0,
|
||||
targetType: "none",
|
||||
effects: [["onDraw", "self", ctx.effect, 1]],
|
||||
},
|
||||
};
|
||||
draft.player.deck.cards[cardId] = card;
|
||||
moveToRegion(card, null, draft.player.deck.regions.drawPile);
|
||||
});
|
||||
} else if (ctx.effect.id === "summonMummy") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
for (const enemyData of getAllEnemyData(ctx.game)) {
|
||||
if (enemyData.id === "木乃伊") {
|
||||
const existingMummy = draft.enemies.find(e => e.enemy.id === "木乃伊" && !e.isAlive);
|
||||
if (existingMummy) {
|
||||
existingMummy.isAlive = true;
|
||||
existingMummy.hp = existingMummy.maxHp;
|
||||
return;
|
||||
}
|
||||
const intent = enemyData.intents.find(i => i.initialIntent) ?? enemyData.intents[0];
|
||||
const instanceId = `${enemyData.id}-${draft.enemies.length}`;
|
||||
const intents: Record<string, typeof intent> = {};
|
||||
for (const i of enemyData.intents) {
|
||||
intents[i.id] = i;
|
||||
}
|
||||
draft.enemies.push({
|
||||
id: instanceId,
|
||||
enemy: enemyData,
|
||||
hp: 14,
|
||||
maxHp: 14,
|
||||
isAlive: true,
|
||||
effects: {},
|
||||
intents,
|
||||
currentIntent: intent,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (ctx.effect.id === "summonSandwormLarva") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
for (const enemyData of getAllEnemyData(ctx.game)) {
|
||||
if (enemyData.id === "幼沙虫") {
|
||||
const intent = enemyData.intents.find(i => i.initialIntent) ?? enemyData.intents[0];
|
||||
const instanceId = `${enemyData.id}-${draft.enemies.length}`;
|
||||
const intents: Record<string, typeof intent> = {};
|
||||
for (const i of enemyData.intents) {
|
||||
intents[i.id] = i;
|
||||
}
|
||||
draft.enemies.push({
|
||||
id: instanceId,
|
||||
enemy: enemyData,
|
||||
hp: 18,
|
||||
maxHp: 18,
|
||||
isAlive: true,
|
||||
effects: {},
|
||||
intents,
|
||||
currentIntent: intent,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (ctx.effect.id === "reviveMummy") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const deadMummy = draft.enemies.find(e => e.enemy.id === "木乃伊" && !e.isAlive);
|
||||
if (deadMummy) {
|
||||
deadMummy.isAlive = true;
|
||||
deadMummy.hp = deadMummy.maxHp;
|
||||
}
|
||||
});
|
||||
} else if (ctx.effect.id === "curse") {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
addEntityEffect(draft.player, ctx.effect, ctx.stacks);
|
||||
});
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// blocks
|
||||
// ========== block / damage prevention ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (!entity) return;
|
||||
|
||||
const preventable = (ctx.amount - (ctx.prevented ?? 0));
|
||||
const blocks = entity.effects.block?.stacks ?? 0;
|
||||
let preventable = ctx.amount - (ctx.prevented ?? 0);
|
||||
|
||||
const blocks = entity.effects.defend?.stacks ?? 0;
|
||||
const blocked = Math.min(blocks, preventable);
|
||||
if (blocked) {
|
||||
ctx.prevented = (ctx.prevented ?? 0) + blocked;
|
||||
preventable -= blocked;
|
||||
}
|
||||
|
||||
const damageReduce = entity.effects.damageReduce?.stacks ?? 0;
|
||||
if (damageReduce > 0) {
|
||||
const reduced = Math.min(damageReduce, preventable);
|
||||
ctx.prevented = (ctx.prevented ?? 0) + reduced;
|
||||
preventable -= reduced;
|
||||
}
|
||||
|
||||
const expose = entity.effects.expose?.stacks ?? 0;
|
||||
if (expose > 0) {
|
||||
ctx.amount += expose;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// ========== spike: damage attacker ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.amount - (ctx.prevented ?? 0) <= 0) return;
|
||||
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (!entity || !entity.isAlive) return;
|
||||
|
||||
const spike = entity.effects.spike?.stacks ?? 0;
|
||||
if (spike > 0 && ctx.sourceEntityKey) {
|
||||
await triggers.onDamage.execute(ctx.game, {
|
||||
entityKey: ctx.sourceEntityKey,
|
||||
amount: spike,
|
||||
sourceEntityKey: ctx.entityKey,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========== storm: give static card to player on attack ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.amount - (ctx.prevented ?? 0) <= 0) return;
|
||||
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (!entity || !entity.isAlive) return;
|
||||
|
||||
const storm = entity.effects.storm?.stacks ?? 0;
|
||||
if (storm > 0 && ctx.entityKey !== "player") {
|
||||
for (let i = 0; i < storm; i++) {
|
||||
await triggers.onEffectApplied.execute(ctx.game, {
|
||||
effect: findEffect(ctx.game, "static"),
|
||||
entityKey: "player",
|
||||
stacks: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ========== energyDrain: player loses energy when enemy takes damage ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (!entity) return;
|
||||
|
||||
const energyDrain = entity.effects.energyDrain?.stacks ?? 0;
|
||||
if (energyDrain > 0 && ctx.entityKey !== "player") {
|
||||
const dealt = Math.min(Math.max(0, entity.hp), ctx.amount - (ctx.prevented ?? 0));
|
||||
if (dealt > 0) {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
draft.player.energy = Math.max(0, draft.player.energy - energyDrain);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// ========== molt: enemy flees if molt >= maxHp ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (!entity || !entity.isAlive) return;
|
||||
|
||||
const molt = entity.effects.molt?.stacks ?? 0;
|
||||
if (molt >= entity.maxHp) {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const e = draft.enemies.find(en => en.id === ctx.entityKey);
|
||||
if (e) {
|
||||
e.isAlive = false;
|
||||
e.hp = 0;
|
||||
}
|
||||
draft.result = draft.enemies.every(en => !en.isAlive) ? "victory" : null;
|
||||
});
|
||||
if (ctx.game.value.result) throw ctx.game.value;
|
||||
}
|
||||
});
|
||||
|
||||
// ========== discard: random discard at turn start ==========
|
||||
triggers.onTurnStart.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.entityKey !== "player") return;
|
||||
|
||||
const discard = ctx.game.value.player.effects.discard;
|
||||
if (!discard || discard.stacks <= 0) return;
|
||||
|
||||
const handIds = [...ctx.game.value.player.deck.regions.hand.childIds];
|
||||
if (handIds.length === 0) return;
|
||||
|
||||
const randomIndex = ctx.game.rng.nextInt(handIds.length);
|
||||
const randomCardId = handIds[randomIndex];
|
||||
await triggers.onCardDiscarded.execute(ctx.game, { cardId: randomCardId });
|
||||
});
|
||||
|
||||
// ========== defendNext: gain block next turn ==========
|
||||
triggers.onTurnStart.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.entityKey !== "player") return;
|
||||
|
||||
const defendNext = ctx.game.value.player.effects.defendNext;
|
||||
if (!defendNext || defendNext.stacks <= 0) return;
|
||||
|
||||
await ctx.game.produceAsync(draft => {
|
||||
addEntityEffect(draft.player, findEffect(ctx.game, "defend"), defendNext.stacks);
|
||||
addEntityEffect(draft.player, defendNext.data, -defendNext.stacks);
|
||||
});
|
||||
});
|
||||
|
||||
// ========== energyNext: gain energy next turn ==========
|
||||
triggers.onTurnStart.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.entityKey !== "player") return;
|
||||
|
||||
const energyNext = ctx.game.value.player.effects.energyNext;
|
||||
if (!energyNext || energyNext.stacks <= 0) return;
|
||||
|
||||
await ctx.game.produceAsync(draft => {
|
||||
draft.player.energy += energyNext.stacks;
|
||||
addEntityEffect(draft.player, energyNext.data, -energyNext.stacks);
|
||||
});
|
||||
});
|
||||
|
||||
// ========== drawNext: draw extra cards next turn ==========
|
||||
triggers.onTurnStart.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.entityKey !== "player") return;
|
||||
|
||||
const drawNext = ctx.game.value.player.effects.drawNext;
|
||||
if (!drawNext || drawNext.stacks <= 0) return;
|
||||
|
||||
await ctx.game.produceAsync(draft => {
|
||||
addEntityEffect(draft.player, drawNext.data, -drawNext.stacks);
|
||||
});
|
||||
await triggers.onDraw.execute(ctx.game, { count: drawNext.stacks });
|
||||
});
|
||||
|
||||
// ========== aim: double damage, lose aim on damage ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
if (ctx.sourceEntityKey === "player") {
|
||||
const player = ctx.game.value.player;
|
||||
const aim = player.effects.aim?.stacks ?? 0;
|
||||
if (aim > 0) {
|
||||
ctx.amount *= 2;
|
||||
}
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// ========== roll: consume 10 roll per 10 damage ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
if (ctx.sourceEntityKey === "player") {
|
||||
const player = ctx.game.value.player;
|
||||
const roll = player.effects.roll?.stacks ?? 0;
|
||||
if (roll >= 10) {
|
||||
const rollDamage = Math.floor(roll / 10) * 10;
|
||||
ctx.amount += rollDamage;
|
||||
await ctx.game.produceAsync(draft => {
|
||||
addEntityEffect(draft.player, findEffect(ctx.game, "roll"), -rollDamage);
|
||||
});
|
||||
}
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// ========== tailSting: bonus damage on attack ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
if (ctx.sourceEntityKey && ctx.sourceEntityKey !== "player") {
|
||||
const attacker = getCombatEntity(ctx.game.value, ctx.sourceEntityKey);
|
||||
if (attacker) {
|
||||
const tailSting = attacker.effects.tailSting?.stacks ?? 0;
|
||||
if (tailSting > 0) {
|
||||
ctx.amount += tailSting;
|
||||
}
|
||||
}
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// ========== charge: double damage dealt/received, consume equal charge ==========
|
||||
triggers.onDamage.use(async (ctx, next) => {
|
||||
const entity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
if (entity) {
|
||||
const charge = entity.effects.charge?.stacks ?? 0;
|
||||
if (charge > 0) {
|
||||
const dealt = Math.min(Math.max(0, entity.hp), ctx.amount - (ctx.prevented ?? 0));
|
||||
const consumed = Math.min(charge, dealt);
|
||||
ctx.amount += dealt;
|
||||
if (consumed > 0) {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const e = getCombatEntity(draft, ctx.entityKey);
|
||||
if (e) addEntityEffect(e, findEffect(ctx.game, "charge"), -consumed);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.sourceEntityKey) {
|
||||
const attacker = getCombatEntity(ctx.game.value, ctx.sourceEntityKey);
|
||||
if (attacker) {
|
||||
const charge = attacker.effects.charge?.stacks ?? 0;
|
||||
if (charge > 0) {
|
||||
const baseAmount = ctx.amount;
|
||||
const targetEntity = getCombatEntity(ctx.game.value, ctx.entityKey);
|
||||
const dealt = Math.min(Math.max(0, targetEntity?.hp ?? 0), baseAmount - (ctx.prevented ?? 0));
|
||||
const consumed = Math.min(charge, dealt);
|
||||
ctx.amount += dealt;
|
||||
if (consumed > 0) {
|
||||
await ctx.game.produceAsync(draft => {
|
||||
const a = getCombatEntity(draft, ctx.sourceEntityKey!);
|
||||
if (a) addEntityEffect(a, findEffect(ctx.game, "charge"), -consumed);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
function getAllEnemyData(game: CombatGameContext) {
|
||||
const seen = new Set<string>();
|
||||
const result: typeof game.value.enemies[number]["enemy"][] = [];
|
||||
for (const enemy of game.value.enemies) {
|
||||
if (!seen.has(enemy.enemy.id)) {
|
||||
seen.add(enemy.enemy.id);
|
||||
result.push(enemy.enemy);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function findEffect(game: CombatGameContext | { value: CombatGameContext["value"] }, id: string): EffectData {
|
||||
const value = "value" in game ? game.value : game;
|
||||
const dataModule = (globalThis as any).__desertEffects;
|
||||
if (dataModule) {
|
||||
const found = dataModule.find((e: EffectData) => e.id === id);
|
||||
if (found) return found;
|
||||
}
|
||||
return { id, name: id, description: "", lifecycle: "instant" } as EffectData;
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// Types
|
||||
export type {
|
||||
EffectData,
|
||||
EffectLifecycle,
|
||||
EnemyData,
|
||||
CardType,
|
||||
CardCostType,
|
||||
CardTargetType,
|
||||
EffectTarget,
|
||||
CardData,
|
||||
CardEffectTrigger,
|
||||
CardEffectTarget,
|
||||
EncounterType,
|
||||
EncounterData,
|
||||
IntentData,
|
||||
ItemData,
|
||||
} from './system/types';
|
||||
|
||||
// Deck
|
||||
export type { GameCard, GameCardMeta, PlayerDeck, DeckRegions } from './system/deck';
|
||||
export {
|
||||
generateDeckFromInventory,
|
||||
createCard,
|
||||
createPlayerDeck,
|
||||
generateCardId,
|
||||
} from './system/deck';
|
||||
|
||||
// Grid Inventory
|
||||
export type { CellCoordinate, CellKey, GridInventory, InventoryItem, MutationResult, PlacementResult } from './system/grid-inventory';
|
||||
export {
|
||||
createGridInventory,
|
||||
flipItem,
|
||||
getAdjacentItems,
|
||||
getItemAtCell,
|
||||
getOccupiedCellSet,
|
||||
moveItem,
|
||||
placeItem,
|
||||
removeItem as removeItemFromGrid,
|
||||
rotateItem,
|
||||
validatePlacement,
|
||||
} from './system/grid-inventory';
|
||||
|
||||
// Map
|
||||
export { MapNodeType, MapLayerType } from './system/map';
|
||||
export type { MapNode, MapLayer, PointCrawlMap, MapGenerationConfig } from './system/map';
|
||||
export { generatePointCrawlMap, getNode, getChildren, getParents, findAllPaths } from './system/map';
|
||||
|
||||
// Progress / Run
|
||||
export type {
|
||||
EncounterResult,
|
||||
EncounterState,
|
||||
GameItem,
|
||||
GameItemMeta,
|
||||
PlayerState,
|
||||
RunMutationResult,
|
||||
RunState,
|
||||
} from './system/progress';
|
||||
export {
|
||||
assignEncounterToNode,
|
||||
assignEncountersFromPool,
|
||||
assignAllEncounters,
|
||||
buildCombatState,
|
||||
createEnemyEntities,
|
||||
getCurrentEncounterData,
|
||||
isCombatEncounter,
|
||||
startEncounter,
|
||||
resolveCombatEncounter,
|
||||
createRunState,
|
||||
canMoveTo,
|
||||
moveToNode,
|
||||
resolveEncounter,
|
||||
isEncounterResolved,
|
||||
damagePlayer,
|
||||
healPlayer,
|
||||
setMaxHp,
|
||||
addGold,
|
||||
spendGold,
|
||||
addItem,
|
||||
removeItem,
|
||||
getCurrentNode,
|
||||
getReachableChildren,
|
||||
getUnresolvedChildren,
|
||||
isAtStartNode,
|
||||
isAtEndNode,
|
||||
} from './system/progress';
|
||||
|
||||
// Combat
|
||||
export type {
|
||||
EffectTable,
|
||||
CombatEntity,
|
||||
PlayerEntity,
|
||||
EnemyEntity,
|
||||
CombatPhase,
|
||||
CombatResult,
|
||||
LootEntry,
|
||||
CombatState,
|
||||
CombatGameContext,
|
||||
} from './system/combat/types';
|
||||
export {
|
||||
addEffect,
|
||||
addEntityEffect,
|
||||
addItemEffect,
|
||||
onEntityEffectUpkeep,
|
||||
onEntityPostureDamage,
|
||||
onPlayerItemEffectUpkeep,
|
||||
onItemPlay,
|
||||
onItemDiscard,
|
||||
getAliveEnemies,
|
||||
getEffectTargets,
|
||||
getCombatEntity,
|
||||
canPlayCard,
|
||||
payCardCost,
|
||||
} from './system/combat/effects';
|
||||
export { prompts as combatPrompts, promptMainAction } from './system/combat/prompts';
|
||||
export { createStartWith, type Triggers } from './system/combat/triggers';
|
||||
|
||||
// Utils
|
||||
export { parseShapeString, type ParsedShape } from './system/utils/parse-shape';
|
||||
export {
|
||||
IDENTITY_TRANSFORM,
|
||||
type Transform2D,
|
||||
type Point2D,
|
||||
getOccupiedCells,
|
||||
transformPoint,
|
||||
transformShape,
|
||||
checkCollision,
|
||||
checkBoardCollision,
|
||||
checkBounds,
|
||||
rotateTransform,
|
||||
flipXTransform,
|
||||
flipYTransform,
|
||||
} from './system/utils/shape-collision';
|
||||
|
||||
// Data
|
||||
export { default as data } from './data';
|
||||
|
|
@ -18,14 +18,14 @@ type TriggerTypes = {
|
|||
onTurnStart: { entityKey: "player" | string, },
|
||||
onTurnEnd: { entityKey: "player" | string, },
|
||||
onShuffle: {},
|
||||
onCardPlayed: { cardId: string, targetId?: string },
|
||||
onCardDiscarded: { cardId: string, },
|
||||
onCardDrawn: { cardId: string, },
|
||||
onCardPlayed: { cardId: string, targetId?: string, sourceEntityKey?: "player" | string },
|
||||
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 },
|
||||
onEffectApplied: { effect: EffectData, entityKey: "player" | string, stacks: number, cardId?: string, sourceEntityKey?: "player" | string },
|
||||
onHpChange: { entityKey: "player" | string, amount: number},
|
||||
onDamage: { entityKey: "player" | string, amount: number, prevented?: number},
|
||||
onEnemyIntent: { enemyId: string },
|
||||
onDamage: { entityKey: "player" | string, amount: number, prevented?: number, sourceEntityKey?: "player" | string},
|
||||
onEnemyIntent: { enemyId: string, sourceEntityKey?: "player" | string },
|
||||
onIntentUpdate: { enemyId: string },
|
||||
}
|
||||
|
||||
|
|
@ -67,10 +67,11 @@ function createTriggers(){
|
|||
});
|
||||
const {cards, regions} = ctx.game.value.player.deck;
|
||||
const card = cards[ctx.cardId];
|
||||
const source = ctx.sourceEntityKey ?? "player";
|
||||
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});
|
||||
await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId, sourceEntityKey: source});
|
||||
}
|
||||
}),
|
||||
onCardDiscarded: createTrigger("onCardDiscarded", async ctx => {
|
||||
|
|
@ -81,10 +82,11 @@ function createTriggers(){
|
|||
});
|
||||
const {cards, regions} = ctx.game.value.player.deck;
|
||||
const card = cards[ctx.cardId];
|
||||
const source = ctx.sourceEntityKey ?? "player";
|
||||
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});
|
||||
await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId, sourceEntityKey: source});
|
||||
}
|
||||
}),
|
||||
onCardDrawn: createTrigger("onCardDrawn", async ctx => {
|
||||
|
|
@ -94,10 +96,11 @@ function createTriggers(){
|
|||
});
|
||||
const {cards, regions} = ctx.game.value.player.deck;
|
||||
const card = cards[ctx.cardId];
|
||||
const source = ctx.sourceEntityKey ?? "player";
|
||||
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});
|
||||
await triggers.onEffectApplied.execute(ctx.game,{effect, entityKey: entity.id, stacks, cardId: ctx.cardId, sourceEntityKey: source});
|
||||
}
|
||||
}),
|
||||
onDraw: createTrigger("onDraw", async ctx => {
|
||||
|
|
@ -162,9 +165,10 @@ function createTriggers(){
|
|||
const intent = enemy.currentIntent;
|
||||
if(!intent) return;
|
||||
|
||||
const source = ctx.sourceEntityKey ?? enemy.id;
|
||||
for(const [target, effect, stacks] of intent.effects){
|
||||
for(const entity of getEffectTargets(target, ctx.game))
|
||||
await triggers.onEffectApplied.execute(ctx.game, { effect, entityKey: entity.id, stacks, });
|
||||
await triggers.onEffectApplied.execute(ctx.game, { effect, entityKey: entity.id, stacks, sourceEntityKey: source });
|
||||
}
|
||||
}),
|
||||
onIntentUpdate: createTrigger("onIntentUpdate", async ctx => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue