diff --git a/src/samples/slay-the-spire-like/index.ts b/src/samples/slay-the-spire-like/index.ts index b211e0b..115b5d7 100644 --- a/src/samples/slay-the-spire-like/index.ts +++ b/src/samples/slay-the-spire-like/index.ts @@ -1,130 +1,6 @@ -// 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, - GameItem, - GameItemMeta, -} from "./system/grid-inventory"; -export { - createGridInventory, - flipItem, - getAdjacentItems, - getItemAtCell, - getOccupiedCellSet, - moveItem, - placeItem, - removeItem as removeItemFromGrid, - rotateItem, - validatePlacement, - createItemIn, -} 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 { EncounterState, RunState } from "./system/encounter"; -export { buildCombatState } from "./system/encounter"; - -// 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 type { ContentModule } from "./data"; -export { default as data } from "./data"; +export * from "./system/combat"; +export * from "./system/deck"; +export * from "./system/encounter"; +export * from "./system/grid-inventory"; +export * from "./system/map"; +export * from "./system/utils/parse-shape"; diff --git a/src/samples/slay-the-spire-like/system/combat/index.ts b/src/samples/slay-the-spire-like/system/combat/index.ts new file mode 100644 index 0000000..9a0799e --- /dev/null +++ b/src/samples/slay-the-spire-like/system/combat/index.ts @@ -0,0 +1,5 @@ +export * from "./effects"; +export * from "./factory"; +export * from "./prompts"; +export * from "./triggers"; +export * from "./types"; diff --git a/src/samples/slay-the-spire-like/system/encounter/encounter.ts b/src/samples/slay-the-spire-like/system/encounter/encounter.ts deleted file mode 100644 index 2a70799..0000000 --- a/src/samples/slay-the-spire-like/system/encounter/encounter.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { createGridInventory, placeItem } from "../grid-inventory"; -import { GameItemMeta } from "../grid-inventory/types"; -import { IDENTITY_TRANSFORM } from "../utils/shape-collision"; -import { parseShapeString } from "../utils/parse-shape"; -import { EncounterData, EncounterType, ItemData } from "../types"; -import type { RNG } from "@/utils/rng"; -import type { - CampEncounterState, - CombatEncounterState, - CurioEncounterState, - DialogueEncounterState, - EncounterState, - ShopEncounterState, -} from "./types"; -import { buildCombatEncounterState } from "./combat"; -import { buildShopEncounterState } from "./shop"; - -function createCurioItems(allItems: ItemData[], rng: RNG): GameItemMeta[] { - const curioItems: GameItemMeta[] = []; - const rolledIndices = new Set(); - - for (let i = 0; i < 3 && rolledIndices.size < allItems.length; i++) { - let index: number; - do { - index = rng.nextInt(allItems.length); - } while (rolledIndices.has(index)); - rolledIndices.add(index); - - const itemData = allItems[index]; - const shape = parseShapeString(itemData.shape); - curioItems.push({ itemData, shape }); - } - - return curioItems; -} - -export function buildCurioEncounterState( - data: EncounterData<"curio">, - allItems: ItemData[], - rng: RNG, -): CurioEncounterState { - const items = createCurioItems(allItems, rng); - const inventory = createGridInventory(6, 4); - - for (let i = 0; i < items.length; i++) { - const meta = items[i]; - placeItem(inventory, { - id: `curio-item-${i}`, - shape: meta.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: i, y: 0 } }, - meta, - }); - } - - return { data, items: inventory }; -} - -export function buildCampEncounterState( - data: EncounterData<"camp">, -): CampEncounterState { - return { data }; -} - -export function buildDialogueEncounterState( - data: EncounterData<"event">, -): DialogueEncounterState { - return { data, blocked: false }; -} - -export function buildEncounterState( - data: EncounterData, - allItems: ItemData[], - rng: RNG, - idCounter: { value: number }, -): EncounterState { - switch (data.type) { - case "minion": - case "elite": - return buildCombatEncounterState( - data as EncounterData<"minion" | "elite">, - ); - case "shop": - return buildShopEncounterState( - data as EncounterData<"shop">, - allItems, - rng, - idCounter, - ); - case "curio": - return buildCurioEncounterState( - data as EncounterData<"curio">, - allItems, - rng, - ); - case "camp": - return buildCampEncounterState(data as EncounterData<"camp">); - case "event": - return buildDialogueEncounterState(data as EncounterData<"event">); - } -} diff --git a/src/samples/slay-the-spire-like/system/encounter/index.ts b/src/samples/slay-the-spire-like/system/encounter/index.ts index 49136e8..47b607d 100644 --- a/src/samples/slay-the-spire-like/system/encounter/index.ts +++ b/src/samples/slay-the-spire-like/system/encounter/index.ts @@ -1,4 +1,2 @@ -export { buildCombatState, buildCombatEncounterState } from "./combat"; -export { buildShopEncounterState, generateInstanceId } from "./shop"; -export { buildEncounterState } from "./encounter"; -export { RunState, EncounterState } from "./types"; +export * from "./types"; +export * from "./run"; diff --git a/src/samples/slay-the-spire-like/system/encounter/run.ts b/src/samples/slay-the-spire-like/system/encounter/run.ts index 809f405..bb9d6ab 100644 --- a/src/samples/slay-the-spire-like/system/encounter/run.ts +++ b/src/samples/slay-the-spire-like/system/encounter/run.ts @@ -3,7 +3,7 @@ import { RunState } from "./types"; const DEFAULT_MAX_HP = 50; const DEFAULT_GOLD = 50; -export function createRunState(startNode: string): RunState { +export function createRunState(): RunState { return { maxHp: DEFAULT_MAX_HP, currentHp: DEFAULT_MAX_HP, diff --git a/src/samples/slay-the-spire-like/system/encounter/shop.ts b/src/samples/slay-the-spire-like/system/encounter/shop.ts deleted file mode 100644 index b291c4d..0000000 --- a/src/samples/slay-the-spire-like/system/encounter/shop.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { createGridInventory, placeItem } from "../grid-inventory"; -import { GameItemMeta } from "../grid-inventory/types"; -import { IDENTITY_TRANSFORM } from "../utils/shape-collision"; -import { parseShapeString } from "../utils/parse-shape"; -import { EncounterData, ItemData } from "../types"; -import type { RNG } from "@/utils/rng"; -import type { ShopEncounterState } from "./types"; - -export function generateInstanceId(counter: { value: number }): string { - counter.value++; - return `item-${counter.value}`; -} - -function createShopItems( - allItems: ItemData[], - rng: RNG, -): (GameItemMeta & { sellPrice: number })[] { - const shopItems: (GameItemMeta & { sellPrice: number })[] = []; - const rolledIndices = new Set(); - - for (let i = 0; i < 5 && rolledIndices.size < allItems.length; i++) { - let index: number; - do { - index = rng.nextInt(allItems.length); - } while (rolledIndices.has(index)); - rolledIndices.add(index); - - const itemData = allItems[index]; - const shape = parseShapeString(itemData.shape); - const sellPrice = Math.floor( - (rng.nextInt(5) + rng.nextInt(5) + 1) * 0.2 * itemData.price, - ); - - shopItems.push({ itemData, shape, sellPrice }); - } - - return shopItems; -} - -export function buildShopEncounterState( - data: EncounterData<"shop">, - allItems: ItemData[], - rng: RNG, - idCounter: { value: number }, -): ShopEncounterState { - const items = createShopItems(allItems, rng); - const inventory = createGridInventory( - 6, - 4, - ); - - for (let i = 0; i < items.length; i++) { - const meta = items[i]; - placeItem(inventory, { - id: generateInstanceId(idCounter), - shape: meta.shape, - transform: { ...IDENTITY_TRANSFORM, offset: { x: i, y: 0 } }, - meta, - }); - } - - return { data, items: inventory }; -} diff --git a/src/samples/slay-the-spire-like/system/grid-inventory/factory.ts b/src/samples/slay-the-spire-like/system/grid-inventory/factory.ts index edc4dc6..35c135f 100644 --- a/src/samples/slay-the-spire-like/system/grid-inventory/factory.ts +++ b/src/samples/slay-the-spire-like/system/grid-inventory/factory.ts @@ -1,9 +1,10 @@ import { parseShapeString } from "../utils/parse-shape"; import type { ParsedShape } from "../utils/parse-shape"; -import type { Transform2D } from "../utils/shape-collision"; -import { placeItem, validatePlacement } from "./transform"; +import { IDENTITY_TRANSFORM, type Transform2D } from "../utils/shape-collision"; +import { createGridInventory, placeItem, validatePlacement } from "./transform"; import type { GameItemMeta, GridInventory, MutationResult } from "./types"; import type { ItemData } from "../types"; +import { ReadonlyRNG, RNG } from "@/utils/rng"; /** * Creates and places a GameItemMeta item into the grid inventory. @@ -13,15 +14,16 @@ import type { ItemData } from "../types"; export function createItemIn( inventory: GridInventory, id: string, - itemData: ItemData, + item: ItemData | GameItemMeta, ): MutationResult { + const itemData = "itemData" in item ? item.itemData : item; const shape = parseShapeString(itemData.shape); const transform = findFirstValidPlacement(inventory, shape); if (!transform) { return { success: false, reason: "无可用位置" }; } - const meta: GameItemMeta = { itemData, shape }; + const meta: GameItemMeta = "itemData" in item ? item : { itemData, shape }; placeItem(inventory, { id, shape, transform, meta }); return { success: true }; } @@ -50,3 +52,70 @@ function findFirstValidPlacement( } return null; } + +function generateInstanceId(counter: { value: number }): string { + counter.value++; + return `item-${counter.value}`; +} + +export function createShopInventory( + allItems: ItemData[], + rng: ReadonlyRNG, + counter: { value: number }, +) { + const shopItems: GameItemMeta[] = []; + const rolledIndices = new Set(); + + for (let i = 0; i < 5 && rolledIndices.size < allItems.length; i++) { + let index: number; + do { + index = rng.nextInt(allItems.length); + } while (rolledIndices.has(index)); + rolledIndices.add(index); + + const itemData = allItems[index]; + const shape = parseShapeString(itemData.shape); + const tradePrice = Math.floor( + (rng.nextInt(5) + rng.nextInt(5) + 1) * 0.2 * itemData.price, + ); + + shopItems.push({ itemData, shape, tradePrice }); + } + + const inventory = createGridInventory(6, 4); + + for (let i = 0; i < shopItems.length; i++) { + createItemIn(inventory, generateInstanceId(counter), shopItems[i]); + } + + return inventory; +} + +export function createCurioItems( + allItems: ItemData[], + rng: RNG, + counter: { value: number }, +) { + const curioItems: GameItemMeta[] = []; + const rolledIndices = new Set(); + + for (let i = 0; i < 2 && rolledIndices.size < allItems.length; i++) { + let index: number; + do { + index = rng.nextInt(allItems.length); + } while (rolledIndices.has(index)); + rolledIndices.add(index); + + const itemData = allItems[index]; + const shape = parseShapeString(itemData.shape); + curioItems.push({ itemData, shape }); + } + + const inventory = createGridInventory(4, 3); + + for (let i = 0; i < curioItems.length; i++) { + createItemIn(inventory, generateInstanceId(counter), curioItems[i]); + } + + return inventory; +} diff --git a/src/samples/slay-the-spire-like/system/grid-inventory/index.ts b/src/samples/slay-the-spire-like/system/grid-inventory/index.ts index 42beaa8..7804168 100644 --- a/src/samples/slay-the-spire-like/system/grid-inventory/index.ts +++ b/src/samples/slay-the-spire-like/system/grid-inventory/index.ts @@ -21,4 +21,4 @@ export { validatePlacement, } from "./transform"; -export { createItemIn } from "./factory"; +export * from "./factory";