refactor(slay-the-spire-like): simplify exports and reorganize system
Clean up the sample's entry point by using barrel exports and move encounter logic into the grid-inventory factory to reduce duplication and improve module structure.
This commit is contained in:
parent
9b20f7ad6f
commit
b509284bc6
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./effects";
|
||||
export * from "./factory";
|
||||
export * from "./prompts";
|
||||
export * from "./triggers";
|
||||
export * from "./types";
|
||||
|
|
@ -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<number>();
|
||||
|
||||
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<GameItemMeta>(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<EncounterType>,
|
||||
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">);
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<number>();
|
||||
|
||||
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<GameItemMeta & { sellPrice: number }>(
|
||||
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 };
|
||||
}
|
||||
|
|
@ -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<GameItemMeta>,
|
||||
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<number>();
|
||||
|
||||
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<GameItemMeta>(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<number>();
|
||||
|
||||
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<GameItemMeta>(4, 3);
|
||||
|
||||
for (let i = 0; i < curioItems.length; i++) {
|
||||
createItemIn(inventory, generateInstanceId(counter), curioItems[i]);
|
||||
}
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ export {
|
|||
validatePlacement,
|
||||
} from "./transform";
|
||||
|
||||
export { createItemIn } from "./factory";
|
||||
export * from "./factory";
|
||||
|
|
|
|||
Loading…
Reference in New Issue