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 * from "./system/combat";
|
||||||
export type {
|
export * from "./system/deck";
|
||||||
EffectData,
|
export * from "./system/encounter";
|
||||||
EffectLifecycle,
|
export * from "./system/grid-inventory";
|
||||||
EnemyData,
|
export * from "./system/map";
|
||||||
CardType,
|
export * from "./system/utils/parse-shape";
|
||||||
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";
|
|
||||||
|
|
|
||||||
|
|
@ -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 * from "./types";
|
||||||
export { buildShopEncounterState, generateInstanceId } from "./shop";
|
export * from "./run";
|
||||||
export { buildEncounterState } from "./encounter";
|
|
||||||
export { RunState, EncounterState } from "./types";
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { RunState } from "./types";
|
||||||
const DEFAULT_MAX_HP = 50;
|
const DEFAULT_MAX_HP = 50;
|
||||||
const DEFAULT_GOLD = 50;
|
const DEFAULT_GOLD = 50;
|
||||||
|
|
||||||
export function createRunState(startNode: string): RunState {
|
export function createRunState(): RunState {
|
||||||
return {
|
return {
|
||||||
maxHp: DEFAULT_MAX_HP,
|
maxHp: DEFAULT_MAX_HP,
|
||||||
currentHp: 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 { parseShapeString } from "../utils/parse-shape";
|
||||||
import type { ParsedShape } from "../utils/parse-shape";
|
import type { ParsedShape } from "../utils/parse-shape";
|
||||||
import type { Transform2D } from "../utils/shape-collision";
|
import { IDENTITY_TRANSFORM, type Transform2D } from "../utils/shape-collision";
|
||||||
import { placeItem, validatePlacement } from "./transform";
|
import { createGridInventory, placeItem, validatePlacement } from "./transform";
|
||||||
import type { GameItemMeta, GridInventory, MutationResult } from "./types";
|
import type { GameItemMeta, GridInventory, MutationResult } from "./types";
|
||||||
import type { ItemData } from "../types";
|
import type { ItemData } from "../types";
|
||||||
|
import { ReadonlyRNG, RNG } from "@/utils/rng";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and places a GameItemMeta item into the grid inventory.
|
* Creates and places a GameItemMeta item into the grid inventory.
|
||||||
|
|
@ -13,15 +14,16 @@ import type { ItemData } from "../types";
|
||||||
export function createItemIn(
|
export function createItemIn(
|
||||||
inventory: GridInventory<GameItemMeta>,
|
inventory: GridInventory<GameItemMeta>,
|
||||||
id: string,
|
id: string,
|
||||||
itemData: ItemData,
|
item: ItemData | GameItemMeta,
|
||||||
): MutationResult {
|
): MutationResult {
|
||||||
|
const itemData = "itemData" in item ? item.itemData : item;
|
||||||
const shape = parseShapeString(itemData.shape);
|
const shape = parseShapeString(itemData.shape);
|
||||||
const transform = findFirstValidPlacement(inventory, shape);
|
const transform = findFirstValidPlacement(inventory, shape);
|
||||||
if (!transform) {
|
if (!transform) {
|
||||||
return { success: false, reason: "无可用位置" };
|
return { success: false, reason: "无可用位置" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta: GameItemMeta = { itemData, shape };
|
const meta: GameItemMeta = "itemData" in item ? item : { itemData, shape };
|
||||||
placeItem(inventory, { id, shape, transform, meta });
|
placeItem(inventory, { id, shape, transform, meta });
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
@ -50,3 +52,70 @@ function findFirstValidPlacement(
|
||||||
}
|
}
|
||||||
return null;
|
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,
|
validatePlacement,
|
||||||
} from "./transform";
|
} from "./transform";
|
||||||
|
|
||||||
export { createItemIn } from "./factory";
|
export * from "./factory";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue