refactor: type rewrite
This commit is contained in:
parent
a469b4024a
commit
1c238aec3a
|
|
@ -1,102 +0,0 @@
|
|||
# slay-the-spire-like
|
||||
|
||||
A Slay the Spire + Backpack Heroes hybrid roguelike sample. Players explore a point-crawl map, manage a tetris-style grid inventory, and fight enemies using cards generated from their equipment.
|
||||
|
||||
## Game Design Docs
|
||||
|
||||
Design docs are in the markdown files at this level:
|
||||
- `01-overview.md` — core game concept, zones, encounter structure, combat rules, buff/debuff system
|
||||
- `02-fighter.md` — Fighter class items (weapons, armor, tools, consumables, relics)
|
||||
- `03-desert.md` — Desert zone enemies (minions, elites, boss)
|
||||
- `data/rules.md` — combat state machine, turn order, effect timing rules
|
||||
|
||||
## Module Structure
|
||||
|
||||
This is **not** a `GameModule` yet — there is no `createInitialState`/`start`/`registry` wired up to `createGameHost`. The code is a library of subsystems that can be composed into a game module.
|
||||
|
||||
### Subsystems
|
||||
|
||||
| Directory | Purpose | Key exports |
|
||||
|-----------|---------|-------------|
|
||||
| `progress/` | Run state, player HP/gold, inventory management, map progression | `createRunState`, `moveToNode`, `resolveEncounter`, `damagePlayer`, `healPlayer`, `addItemFromCsv`, `removeItem`, `getReachableChildren` |
|
||||
| `map/` | Point-crawl map generation and traversal | `generatePointCrawlMap`, `getNode`, `getChildren`, `getParents`, `hasPath`, `findAllPaths` |
|
||||
| `grid-inventory/` | Tetris-style grid placement (place, move, rotate, flip items) | `createGridInventory`, `placeItem`, `removeItem`, `moveItem`, `rotateItem`, `flipItem`, `validatePlacement`, `getAdjacentItems` |
|
||||
| `deck/` | Card/deck system (draw pile, hand, discard, exhaust) | `generateDeckFromInventory`, `createStatusCard`, `createDeckRegions`, `createPlayerDeck` |
|
||||
| `data/` | CSV game data loaded via `inline-schema/csv-loader`. `.d.ts` files are auto-generated by the csv-loader plugin — do not edit by hand. | `heroItemFighter1Data`, `encounterDesertData`, `enemyDesertData`, `enemyIntentDesertData`, `effectDesertData`, `statusCardDesertData` |
|
||||
| `dialogue/` | Yarn Spinner dialogue files (placeholder). Loaded via `yarn-spinner-loader`, a local peer dependency at `../yarn-spinner-loader` (like `inline-schema`, it can be changed and published if needed). | `encounters` yarnproject |
|
||||
| `utils/` | Shape parsing and collision math | `parseShapeString`, `checkCollision`, `checkBounds`, `transformShape`, `rotateTransform`, `flipXTransform`, `flipYTransform` |
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
CSV files (data/)
|
||||
→ inline-schema/csv-loader → typed JS objects (e.g. HeroItemFighter1)
|
||||
→ parseShapeString() converts shape strings → ParsedShape
|
||||
→ GridInventory<GameItemMeta> holds placed items
|
||||
→ generateDeckFromInventory() generates cards per occupied cell
|
||||
```
|
||||
|
||||
### Key types
|
||||
|
||||
- **`RunState`** — top-level state: seed, map, player, inventory, currentNodeId, encounter state, resolved set. Designed for `MutableSignal.produce()` mutation.
|
||||
- **`GridInventory<TMeta>`** — `items: Map<string, InventoryItem<TMeta>>` + `occupiedCells: Set<CellKey>` for O(1) collision. Mutated directly inside `.produce()`.
|
||||
- **`InventoryItem<TMeta>`** — id, shape (ParsedShape), transform (Transform2D), meta. Shape + transform determines which cells are occupied.
|
||||
- **`GameCard`** — a `Part<GameCardMeta>` bridging inventory items to the deck system. `sourceItemId` links back to the inventory item; `null` for status cards.
|
||||
- **`PointCrawlMap`** — layered DAG: 10 layers (start → wild×2 → settlement → wild×2 → settlement → wild×2 → end). Wild = 3 nodes, Settlement = 4 nodes.
|
||||
- **`MapNode`** — id, type (MapNodeType enum), childIds, optional encounter data from CSV.
|
||||
|
||||
### Map generation
|
||||
|
||||
`generatePointCrawlMap(seed?)` produces a deterministic map:
|
||||
- 10 layers: Start → Wild(3) → Wild(3) → Settlement(4) → Wild(3) → Wild(3) → Settlement(4) → Wild(3) → Wild(3) → End
|
||||
- Settlement layers guarantee ≥1 camp, ≥1 shop, ≥1 curio (4th slot random)
|
||||
- Wild pair types are optimized to minimize same-type repetition
|
||||
- Edge patterns avoid crossings: Start→all wild, Wild→Wild 1:1, Wild↔Settlement 3:4 or 4:3, Wild→all End
|
||||
|
||||
### Shape system
|
||||
|
||||
Items have shapes defined as movement strings parsed by `parseShapeString`:
|
||||
- `o` = origin cell, `n/s/e/w` = move + fill, `r` = return to previous position
|
||||
- Example: `"oesw"` = 2×2 block (origin, east, south, west = full square)
|
||||
- Example: `"oe"` = 1×2 horizontal
|
||||
- Example: `"onrersrw"` = cross/X shape
|
||||
|
||||
Shapes are positioned via `Transform2D` (offset, rotation, flipX, flipY) and validated against the 6×4 grid.
|
||||
|
||||
### Grid inventory
|
||||
|
||||
All mutation functions (`placeItem`, `removeItem`, `moveItem`, `rotateItem`, `flipItem`) mutate the `GridInventory` **directly** — they must be called inside `produce()` callbacks. `validatePlacement` checks bounds + collisions before placement.
|
||||
|
||||
### Card generation
|
||||
|
||||
`generateDeckFromInventory(inventory)` creates one card per occupied cell in each item's shape. Cards carry `GameCardMeta` linking back to the source item and cell position. Status cards (wound, venom, etc.) are created separately via `createStatusCard`.
|
||||
|
||||
## CSV data format
|
||||
|
||||
All CSVs use `inline-schema` typed headers. The first row is a comment header, the second row is the schema row with types and references:
|
||||
- `'energy'|'uses'` — union type
|
||||
- `@enemyDesert` — foreign key reference to another CSV
|
||||
- `[effect: @effectDesert; number][]` — array of structured references
|
||||
|
||||
### heroItemFighter1.csv columns
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| type | `'weapon'|'armor'|'consumable'|'tool'` | |
|
||||
| name | string | Display name (Chinese) |
|
||||
| shape | string | Movement string for `parseShapeString` |
|
||||
| costType | `'energy'|'uses'` | Energy = per-turn cost; Uses = limited uses |
|
||||
| costCount | int | Cost amount |
|
||||
| targetType | `'single'|'none'` | |
|
||||
| price | int | Shop price |
|
||||
| desc | string | Ability description (Chinese) |
|
||||
| effects | `['self'|'target'|'all'|'random'; @effectDesert; number][]` | Effect references |
|
||||
|
||||
## Conventions
|
||||
|
||||
- Chinese is used for all user-facing strings (item names, error messages, effect descriptions)
|
||||
- Discriminated union result types: `{ success: true } | { success: false, reason: string }`
|
||||
- Mutation functions mutate state directly (inside `produce()`); validation is separate
|
||||
- `Map` and `Set` are used in `GridInventory` and `PointCrawlMap` (not plain objects) — requires careful handling with `mutative` since it drafts Maps/Sets differently than plain objects
|
||||
- Starter items defined in `progress/index.ts`: `['治疗药剂', '绷带', '水袋', '短刀', '剑']`
|
||||
- Default player stats: 50 HP, 50 gold, 6×4 inventory
|
||||
|
|
@ -41,4 +41,9 @@ export function onPlayerItemEffectUpkeep(entity: PlayerEntity){
|
|||
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
export function onDraw(entity: PlayerEntity, cardId:string ){}
|
||||
// TODO
|
||||
export function onDiscard(entity: PlayerEntity, cardId: string){}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ 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";
|
||||
|
|
@ -12,7 +13,7 @@ type TriggerTypes = {
|
|||
onTurnStart: { entityKey: "player" | string, },
|
||||
onTurnEnd: { entityKey: "player" | string, },
|
||||
onShuffle: { entityKey: "player" | string, },
|
||||
onCardPlayed: { cardId: string, },
|
||||
onCardPlayed: { cardId: string, targetId?: string },
|
||||
onCardDiscarded: { cardId: string, },
|
||||
onCardDrawn: { cardId: string, },
|
||||
onEffectApplied: { effectId: string, entityKey: "player" | string, stacks: number, },
|
||||
|
|
@ -37,6 +38,8 @@ export function createStartWith(build: (triggers: Triggers) => void){
|
|||
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 => {
|
||||
|
|
@ -46,11 +49,23 @@ export function createStartWith(build: (triggers: Triggers) => void){
|
|||
while(true){
|
||||
const action = await promptMainAction(game);
|
||||
if(action.action === "end-turn") break;
|
||||
//TODO resolve action here
|
||||
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});
|
||||
}
|
||||
// TODO discard cards here
|
||||
await triggers.onTurnEnd.execute(game,{entityKey: "player"});
|
||||
// TODO recover energy, draw new cards here
|
||||
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});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import type { PlayerDeck } from "../deck/types";
|
||||
import {EnemyData, IntentData} from "@/samples/slay-the-spire-like/system/types";
|
||||
import {EffectData} from "@/samples/slay-the-spire-like/system/types";
|
||||
import {GridInventory} from "@/samples/slay-the-spire-like/system/grid-inventory";
|
||||
import {GameItemMeta} from "@/samples/slay-the-spire-like/system/progress";
|
||||
|
||||
export type EffectTable = Record<string, {data: EffectData, stacks: number}>;
|
||||
|
||||
|
|
@ -39,6 +41,7 @@ export type LootEntry = {
|
|||
export type CombatState = {
|
||||
enemies: EnemyEntity[];
|
||||
player: PlayerEntity;
|
||||
inventory: GridInventory<GameItemMeta>;
|
||||
|
||||
phase: CombatPhase;
|
||||
turnNumber: number;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {CombatState} from "@/samples/slay-the-spire-like/system";
|
||||
import {CombatState} from "./types";
|
||||
|
||||
export function* getAliveEnemies(state: CombatState) {
|
||||
for (let enemy of state.enemies) {
|
||||
|
|
|
|||
|
|
@ -1,188 +1,66 @@
|
|||
import type { CellKey, GridInventory, InventoryItem } from '../grid-inventory/types';
|
||||
import {moveToRegion } from '@/core/region';
|
||||
import { createRegion } from '@/core/region';
|
||||
import type { GridInventory } from '../grid-inventory/types';
|
||||
import type { GameItemMeta } from '../progress/types';
|
||||
import { createRegion, createRegionAxis } from '@/core/region';
|
||||
import type { GameCard, GameCardMeta, PlayerDeck, DeckRegions } from './types';
|
||||
import { cardDesertData } from '../data';
|
||||
import type { CardData } from '../types';
|
||||
import type {DeckRegions, GameCard, PlayerDeck} from './types';
|
||||
|
||||
/**
|
||||
* Generates a unique card ID for a cell within an item.
|
||||
*/
|
||||
function generateCardId(itemId: string, cellIndex: number): string {
|
||||
return `card-${itemId}-${cellIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all cell keys from an item's shape in a deterministic order.
|
||||
* Iterates the shape grid row by row, left to right, top to bottom.
|
||||
*/
|
||||
function getItemCells(item: InventoryItem<GameItemMeta>): CellKey[] {
|
||||
const cells: CellKey[] = [];
|
||||
const { shape, transform } = item;
|
||||
const { grid } = shape;
|
||||
const { offset, rotation, flipX, flipY } = transform;
|
||||
|
||||
// Track local dimensions (may swap on rotation)
|
||||
let localWidth = grid[0]?.length || 1;
|
||||
let localHeight = grid.length;
|
||||
|
||||
for (let gy = 0; gy < grid.length; gy++) {
|
||||
for (let gx = 0; gx < grid[gy].length; gx++) {
|
||||
if (!grid[gy][gx]) continue;
|
||||
|
||||
// Start from grid coordinates
|
||||
let x = gx;
|
||||
let y = gy;
|
||||
|
||||
// Apply rotation (90 degree increments, clockwise)
|
||||
const rotTimes = ((rotation % 4) + 4) % 4;
|
||||
for (let r = 0; r < rotTimes; r++) {
|
||||
const newX = localHeight - 1 - y;
|
||||
const newY = x;
|
||||
x = newX;
|
||||
y = newY;
|
||||
// Swap dimensions for next iteration
|
||||
const tmp = localWidth;
|
||||
localWidth = localHeight;
|
||||
localHeight = tmp;
|
||||
}
|
||||
|
||||
// Reset local dimensions for fresh computation per cell
|
||||
localWidth = grid[0]?.length || 1;
|
||||
localHeight = grid.length;
|
||||
|
||||
// Apply flips
|
||||
if (flipX) {
|
||||
x = -x;
|
||||
}
|
||||
if (flipY) {
|
||||
y = -y;
|
||||
}
|
||||
|
||||
// Apply offset
|
||||
const finalX = x + offset.x;
|
||||
const finalY = y + offset.y;
|
||||
|
||||
cells.push(`${finalX},${finalY}`);
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single card from an inventory item.
|
||||
*/
|
||||
function createItemCard(
|
||||
itemId: string,
|
||||
itemData: GameItemMeta['itemData'],
|
||||
cellKey: CellKey,
|
||||
cellIndex: number
|
||||
): GameCard {
|
||||
const cardId = generateCardId(itemId, cellIndex);
|
||||
const cardData = cardDesertData.find(c => c.id === itemData.card.id);
|
||||
|
||||
function createCard( itemId: string, cardData: CardData, cellIndex: number): GameCard {
|
||||
return {
|
||||
id: cardId,
|
||||
id: generateCardId(itemId, cellIndex),
|
||||
regionId: '',
|
||||
position: [],
|
||||
sourceItemId: itemId,
|
||||
itemData: cardData ?? null,
|
||||
cellKey,
|
||||
displayName: cardData?.name ?? itemData.name,
|
||||
description: cardData?.desc ?? '',
|
||||
itemId,
|
||||
cardData
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a status card that does not correspond to any inventory item.
|
||||
* Status cards represent temporary effects like wounds, stuns, etc.
|
||||
*/
|
||||
function createStatusCard(
|
||||
id: string,
|
||||
displayName: string,
|
||||
description: string
|
||||
): GameCard {
|
||||
function createDeckRegions(): DeckRegions {
|
||||
return {
|
||||
id,
|
||||
regionId: '',
|
||||
position: [],
|
||||
sourceItemId: null,
|
||||
itemData: null,
|
||||
cellKey: null,
|
||||
displayName,
|
||||
description,
|
||||
drawPile: createRegion('drawPile', []),
|
||||
hand: createRegion('hand', []),
|
||||
discardPile: createRegion('discardPile', []),
|
||||
exhaustPile: createRegion('exhaustPile', []),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a complete player deck from the current inventory state.
|
||||
*/
|
||||
function generateDeckFromInventory(inventory: GridInventory<GameItemMeta>): PlayerDeck {
|
||||
const cards: Record<string, GameCard> = {};
|
||||
const drawPile: string[] = [];
|
||||
const regions = createDeckRegions();
|
||||
|
||||
for (const item of inventory.items.values()) {
|
||||
const itemData = item.meta?.itemData;
|
||||
if (!itemData) continue;
|
||||
|
||||
// Generate one card per occupied cell in the item's shape
|
||||
const cellCount = item.shape.count;
|
||||
const cells = getItemCells(item);
|
||||
|
||||
for (let i = 0; i < cellCount; i++) {
|
||||
const cellKey = cells[i] ?? `${i},0`;
|
||||
const card = createItemCard(item.id, itemData, cellKey, i);
|
||||
const count = item.shape.count;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const card = createCard(item.id, itemData.card, i);
|
||||
cards[card.id] = card;
|
||||
drawPile.push(card.id);
|
||||
moveToRegion(card, null, regions.drawPile);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cards,
|
||||
drawPile,
|
||||
hand: [],
|
||||
discardPile: [],
|
||||
exhaustPile: [],
|
||||
regions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates region definitions for deck management.
|
||||
*/
|
||||
function createDeckRegions(): DeckRegions {
|
||||
return {
|
||||
drawPile: createRegion('drawPile', [
|
||||
createRegionAxis('index', 0, 0),
|
||||
]),
|
||||
hand: createRegion('hand', [
|
||||
createRegionAxis('index', 0, 0),
|
||||
]),
|
||||
discardPile: createRegion('discardPile', [
|
||||
createRegionAxis('index', 0, 0),
|
||||
]),
|
||||
exhaustPile: createRegion('exhaustPile', [
|
||||
createRegionAxis('index', 0, 0),
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty player deck structure.
|
||||
*/
|
||||
function createPlayerDeck(): PlayerDeck {
|
||||
return {
|
||||
cards: {},
|
||||
drawPile: [],
|
||||
hand: [],
|
||||
discardPile: [],
|
||||
exhaustPile: [],
|
||||
regions: createDeckRegions(),
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
generateDeckFromInventory,
|
||||
createStatusCard,
|
||||
createDeckRegions,
|
||||
createCard,
|
||||
createPlayerDeck,
|
||||
createDeckRegions,
|
||||
generateCardId,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
export type { GameCard, GameCardMeta, PlayerDeck, DeckRegions } from './types';
|
||||
export {
|
||||
generateDeckFromInventory,
|
||||
createStatusCard,
|
||||
createDeckRegions,
|
||||
createCard,
|
||||
createPlayerDeck,
|
||||
generateCardId,
|
||||
} from './factory';
|
||||
|
|
|
|||
|
|
@ -1,40 +1,14 @@
|
|||
import type { Part } from '@/core/part';
|
||||
import type { Region } from '@/core/region';
|
||||
import type { HeroItemFighter1 } from '../data/heroItemFighter1.csv';
|
||||
import type { CardDesert } from '../data/cardDesert.csv';
|
||||
import type { CellKey } from '../grid-inventory/types';
|
||||
import {CardData} from "@/samples/slay-the-spire-like/system/types";
|
||||
import {Region} from "@/core/region";
|
||||
|
||||
/**
|
||||
* Metadata for a game card.
|
||||
* Bridges inventory item data with the card system.
|
||||
*/
|
||||
export interface GameCardMeta {
|
||||
/**
|
||||
* Source item instance ID that this card was generated from.
|
||||
* `null` for status cards (e.g. wound, stun) that don't correspond to an inventory item.
|
||||
*/
|
||||
sourceItemId: string | null;
|
||||
/**
|
||||
* Original card data from cardDesert.csv. `null` for status cards not in the CSV.
|
||||
*/
|
||||
itemData: CardDesert | null;
|
||||
/**
|
||||
* The cell key ("x,y") this card represents within the source item's shape.
|
||||
* `null` for status cards.
|
||||
*/
|
||||
cellKey: CellKey | null;
|
||||
/**
|
||||
* Display name of the card.
|
||||
* For item cards: derived from itemData.name.
|
||||
* For status cards: custom name (e.g. "伤口", "眩晕").
|
||||
*/
|
||||
displayName: string;
|
||||
/**
|
||||
* Card description / ability text.
|
||||
* For item cards: derived from itemData.desc.
|
||||
* For status cards: custom description.
|
||||
*/
|
||||
description: string;
|
||||
cardData: CardData;
|
||||
itemId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,26 +23,17 @@ export type GameCard = Part<GameCardMeta>;
|
|||
export interface PlayerDeck {
|
||||
/** All cards indexed by ID */
|
||||
cards: Record<string, GameCard>;
|
||||
/** Card IDs in the draw pile */
|
||||
drawPile: string[];
|
||||
/** Card IDs in the player's hand */
|
||||
hand: string[];
|
||||
/** Card IDs in the discard pile */
|
||||
discardPile: string[];
|
||||
/** Card IDs in the exhaust pile (removed from combat) */
|
||||
exhaustPile: string[];
|
||||
|
||||
regions: DeckRegions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Region structure for deck management.
|
||||
*/
|
||||
export interface DeckRegions {
|
||||
/** Draw pile region */
|
||||
export interface DeckRegions{
|
||||
/** Card IDs in the draw pile */
|
||||
drawPile: Region;
|
||||
/** Hand region */
|
||||
/** Card IDs in the player's hand */
|
||||
hand: Region;
|
||||
/** Discard pile region */
|
||||
/** Card IDs in the discard pile */
|
||||
discardPile: Region;
|
||||
/** Exhaust pile region */
|
||||
/** Card IDs in the exhaust pile (removed from combat) */
|
||||
exhaustPile: Region;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { PointCrawlMap } from '../map/types';
|
||||
import type { GridInventory, InventoryItem } from '../grid-inventory/types';
|
||||
import type { ParsedShape } from '../utils/parse-shape';
|
||||
import type { HeroItemFighter1 } from '../data/heroItemFighter1.csv';
|
||||
import {ItemData} from "@/samples/slay-the-spire-like/system/types";
|
||||
|
||||
/**
|
||||
* Result of an encounter (combat, event, etc.).
|
||||
|
|
@ -35,9 +35,11 @@ export interface EncounterState {
|
|||
*/
|
||||
export interface GameItemMeta {
|
||||
/** Original CSV item data */
|
||||
itemData: HeroItemFighter1;
|
||||
itemData: ItemData;
|
||||
/** Parsed shape for grid placement */
|
||||
shape: ParsedShape;
|
||||
/** Consumed uses, if card cost type is uses**/
|
||||
depletion?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue