fix: encounter data assignment
This commit is contained in:
parent
204198b10f
commit
4fbd65e98c
|
|
@ -20,12 +20,12 @@ function buildEncounterIndex(): Map<string, EncounterDesert[]> {
|
|||
|
||||
/** Map from MapNodeType to encounter type key */
|
||||
const NODE_TYPE_TO_ENCOUNTER: Partial<Record<MapNodeType, string>> = {
|
||||
[MapNodeType.Minion]: 'enemy',
|
||||
[MapNodeType.Minion]: 'minion',
|
||||
[MapNodeType.Elite]: 'elite',
|
||||
[MapNodeType.Event]: 'event',
|
||||
[MapNodeType.Camp]: 'shelter',
|
||||
[MapNodeType.Shop]: 'npc',
|
||||
[MapNodeType.Curio]: 'shelter',
|
||||
[MapNodeType.Camp]: 'camp',
|
||||
[MapNodeType.Shop]: 'shop',
|
||||
[MapNodeType.Curio]: 'curio',
|
||||
};
|
||||
|
||||
/** Default map generation configuration */
|
||||
|
|
@ -125,9 +125,15 @@ export function generatePointCrawlMap(seed?: number): PointCrawlMap {
|
|||
const nodeIds: string[] = [];
|
||||
const layerNodes: MapNode[] = [];
|
||||
|
||||
// Pre-generate settlement types if this is a settlement layer
|
||||
let settlementTypes: MapNodeType[] | undefined;
|
||||
if (structure.layerType === MapLayerType.Settlement) {
|
||||
settlementTypes = generateSettlementTypes(rng);
|
||||
}
|
||||
|
||||
for (let j = 0; j < structure.count; j++) {
|
||||
const id = `node-${i}-${j}`;
|
||||
const type = resolveNodeType(structure.layerType, j, structure.count, rng, wildPairTypes.get(i), j);
|
||||
const type = resolveNodeType(structure.layerType, j, structure.count, rng, wildPairTypes.get(i), j, settlementTypes, j);
|
||||
const encounter = pickEncounterForNode(type, rng);
|
||||
const node: MapNode = {
|
||||
id,
|
||||
|
|
@ -175,7 +181,9 @@ function resolveNodeType(
|
|||
_layerCount: number,
|
||||
rng: RNG,
|
||||
preGeneratedTypes?: MapNodeType[],
|
||||
nodeIndex?: number
|
||||
nodeIndex?: number,
|
||||
settlementTypes?: MapNodeType[],
|
||||
settlementIndex?: number
|
||||
): MapNodeType {
|
||||
switch (layerType) {
|
||||
case 'start':
|
||||
|
|
@ -189,8 +197,11 @@ function resolveNodeType(
|
|||
}
|
||||
return pickWildNodeType(rng);
|
||||
case MapLayerType.Settlement:
|
||||
// This will be overridden by assignSettlementTypes
|
||||
return MapNodeType.Camp; // placeholder
|
||||
// Use pre-generated settlement types if available
|
||||
if (settlementTypes && settlementIndex !== undefined) {
|
||||
return settlementTypes[settlementIndex];
|
||||
}
|
||||
return MapNodeType.Camp; // fallback
|
||||
default:
|
||||
return MapNodeType.Minion;
|
||||
}
|
||||
|
|
@ -295,9 +306,22 @@ function generateOptimalWildPair(
|
|||
return [bestLayer1, bestLayer2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates settlement node types ensuring at least 1 of each: camp, shop, curio.
|
||||
* The 4th node is randomly chosen from the three.
|
||||
* Returns shuffled array of 4 node types.
|
||||
*/
|
||||
function generateSettlementTypes(rng: RNG): MapNodeType[] {
|
||||
const requiredTypes = [MapNodeType.Camp, MapNodeType.Shop, MapNodeType.Curio];
|
||||
const randomType = requiredTypes[rng.nextInt(3)];
|
||||
const types = [...requiredTypes, randomType];
|
||||
return fisherYatesShuffle(types, rng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns settlement node types ensuring at least 1 of each: camp, shop, curio.
|
||||
* The 4th node is randomly chosen from the three.
|
||||
* @deprecated Use generateSettlementTypes() during node creation instead.
|
||||
*/
|
||||
function assignSettlementTypes(nodeIds: string[], nodes: MapNode[], rng: RNG): void {
|
||||
// Shuffle node order to randomize which position gets which type
|
||||
|
|
@ -323,10 +347,8 @@ function generateLayerEdges(
|
|||
nodes: Map<string, MapNode>,
|
||||
rng: RNG
|
||||
): void {
|
||||
// Assign settlement types when creating settlement layer
|
||||
if (targetLayer.layerType === MapLayerType.Settlement) {
|
||||
assignSettlementTypes(targetLayer.nodeIds, targetLayer.nodes, rng);
|
||||
}
|
||||
// Settlement types are now pre-generated during node creation
|
||||
// No need to assign them here anymore
|
||||
|
||||
const sourceType = sourceLayer.layerType;
|
||||
const targetType = targetLayer.layerType;
|
||||
|
|
|
|||
|
|
@ -287,19 +287,36 @@ describe('generatePointCrawlMap', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should assign encounters to nodes', () => {
|
||||
it('should assign encounters to all non-Start/End nodes', () => {
|
||||
const map = generatePointCrawlMap(456);
|
||||
|
||||
let nodesWithEncounter = 0;
|
||||
for (const node of map.nodes.values()) {
|
||||
if (node.encounter) {
|
||||
nodesWithEncounter++;
|
||||
expect(node.encounter.name).toBeTruthy();
|
||||
expect(node.encounter.description).toBeTruthy();
|
||||
if (node.type === MapNodeType.Start || node.type === MapNodeType.End) {
|
||||
// Start and End nodes should not have encounters
|
||||
expect(node.encounter).toBeUndefined();
|
||||
} else {
|
||||
// All other nodes (minion/elite/event/camp/shop/curio) must have encounters
|
||||
expect(node.encounter, `Node ${node.id} (${node.type}) should have encounter data`).toBeDefined();
|
||||
expect(node.encounter!.name).toBeTruthy();
|
||||
expect(node.encounter!.description).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(nodesWithEncounter).toBeGreaterThan(0);
|
||||
it('should assign encounters to all nodes across multiple seeds', () => {
|
||||
// Test multiple seeds to ensure no random failure
|
||||
for (let seed = 0; seed < 20; seed++) {
|
||||
const map = generatePointCrawlMap(seed);
|
||||
|
||||
for (const node of map.nodes.values()) {
|
||||
if (node.type === MapNodeType.Start || node.type === MapNodeType.End) {
|
||||
continue;
|
||||
}
|
||||
expect(node.encounter, `Seed ${seed}: Node ${node.id} (${node.type}) missing encounter`).toBeDefined();
|
||||
expect(node.encounter!.name).toBeTruthy();
|
||||
expect(node.encounter!.description).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should minimize same-layer repetitions in wild layer pairs', () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue