feat(slay-the-spire-like): add map navigation logic

Implement `canMoveTo` and `moveToNode` for navigating the point
crawl map, and add `PointCrawlMapNavigator` type. Also reformat
map module files to use double quotes.
This commit is contained in:
hypercross 2026-04-20 11:59:52 +08:00
parent 88d31430a6
commit 423cc7c841
3 changed files with 102 additions and 51 deletions

View File

@ -1,5 +1,18 @@
export { MapNodeType, MapLayerType } from './types';
export type { MapNode, MapLayer, PointCrawlMap, MapGenerationConfig } from './types';
export { MapNodeType, MapLayerType } from "./types";
export type {
MapNode,
MapLayer,
PointCrawlMap,
MapGenerationConfig,
} from "./types";
export { generatePointCrawlMap } from './generator';
export { getNode, getChildren, getParents, hasPath, findAllPaths } from './generator';
export { generatePointCrawlMap } from "./generator";
export {
getNode,
getChildren,
getParents,
hasPath,
findAllPaths,
} from "./generator";
export { canMoveTo, moveToNode } from "./navigation";

View File

@ -0,0 +1,33 @@
import { getNode } from "./generator";
import { PointCrawlMap, PointCrawlMapNavigator } from "./types";
export function canMoveTo(
navigator: PointCrawlMapNavigator,
map: PointCrawlMap,
targetNodeId: string,
): boolean {
const currentNode = getNode(map, navigator.currentNodeId);
if (!currentNode) return false;
return currentNode.childIds.includes(targetNodeId);
}
export function moveToNode(
navigator: PointCrawlMapNavigator,
map: PointCrawlMap,
targetNodeId: string,
): boolean {
if (!canMoveTo(navigator, map, targetNodeId)) {
return false;
}
const targetNode = getNode(map, targetNodeId);
if (!targetNode) {
return false;
}
// Update current position
navigator.currentNodeId = targetNodeId;
navigator.visitedNodes.add(targetNodeId);
return true;
}

View File

@ -1,83 +1,88 @@
import {EncounterData} from "@/samples/slay-the-spire-like/system/types";
import { EncounterData } from "@/samples/slay-the-spire-like/system/types";
/**
* Types of nodes that can appear on the point crawl map.
*/
export enum MapNodeType {
Start = 'start',
End = 'end',
Minion = 'minion',
Elite = 'elite',
Event = 'event',
Camp = 'camp',
Shop = 'shop',
Curio = 'curio',
Start = "start",
End = "end",
Minion = "minion",
Elite = "elite",
Event = "event",
Camp = "camp",
Shop = "shop",
Curio = "curio",
}
/**
* Semantic type of a layer.
*/
export enum MapLayerType {
Wild = 'wild',
Settlement = 'settlement',
Wild = "wild",
Settlement = "settlement",
}
/**
* A single node on the map.
*/
export interface MapNode {
/** Unique identifier */
id: string;
/** Which layer this node belongs to */
layerIndex: number;
/** Semantic type of the node */
type: MapNodeType;
/** IDs of nodes in the next layer this node connects to */
childIds: string[];
/** Encounter data assigned to this node (from encounter CSV) */
encounter?: EncounterData;
/** Unique identifier */
id: string;
/** Which layer this node belongs to */
layerIndex: number;
/** Semantic type of the node */
type: MapNodeType;
/** IDs of nodes in the next layer this node connects to */
childIds: string[];
/** Encounter data assigned to this node (from encounter CSV) */
encounter?: EncounterData;
}
/**
* A horizontal layer of nodes at the same progression stage.
*/
export interface MapLayer {
/** Layer index (0 = start, last = end) */
index: number;
/** Ordered IDs of nodes in this layer */
nodeIds: string[];
/** Semantic type of the layer */
layerType: MapLayerType | 'start' | 'end';
/** Direct references to nodes in this layer (for performance) */
nodes: MapNode[];
/** Layer index (0 = start, last = end) */
index: number;
/** Ordered IDs of nodes in this layer */
nodeIds: string[];
/** Semantic type of the layer */
layerType: MapLayerType | "start" | "end";
/** Direct references to nodes in this layer (for performance) */
nodes: MapNode[];
}
/**
* A fully generated point crawl map.
*/
export interface PointCrawlMap {
/** Layers from start to end */
layers: MapLayer[];
/** All nodes keyed by ID */
nodes: Map<string, MapNode>;
/** Reverse index: nodeId → parent node IDs (for fast getParent lookup) */
parentIndex?: Map<string, string[]>;
/** Layers from start to end */
layers: MapLayer[];
/** All nodes keyed by ID */
nodes: Map<string, MapNode>;
/** Reverse index: nodeId → parent node IDs (for fast getParent lookup) */
parentIndex?: Map<string, string[]>;
}
export interface PointCrawlMapNavigator {
currentNodeId: string;
visitedNodes: Set<string>;
}
/**
* Configuration for map generation.
*/
export interface MapGenerationConfig {
/** Total number of layers (including start and end) */
totalLayers: number;
/** Number of nodes in each wild layer */
wildLayerNodeCount: number;
/** Number of nodes in each settlement layer */
settlementLayerNodeCount: number;
/** Probability weights for wild node types (should sum to 100) */
wildNodeTypeWeights: {
[MapNodeType.Minion]: number;
[MapNodeType.Elite]: number;
[MapNodeType.Event]: number;
};
/** Total number of layers (including start and end) */
totalLayers: number;
/** Number of nodes in each wild layer */
wildLayerNodeCount: number;
/** Number of nodes in each settlement layer */
settlementLayerNodeCount: number;
/** Probability weights for wild node types (should sum to 100) */
wildNodeTypeWeights: {
[MapNodeType.Minion]: number;
[MapNodeType.Elite]: number;
[MapNodeType.Event]: number;
};
}