k
This commit is contained in:
parent
ca40a0cb73
commit
2c7e525c53
21 changed files with 1730 additions and 285 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { STARTING_CASH, TURN_SECONDS, FARM_SHUTDOWN_GRACE_SECONDS, BUILD_COSTS, FACILITY_DEFS, GRID } from './config.js';
|
||||
import { randomBetween } from './utils.js';
|
||||
import { STARTING_CASH, TURN_SECONDS, FARM_SHUTDOWN_GRACE_SECONDS, BUILD_COSTS, FACILITY_DEFS, GRID, EGG_SPAWN_RANGES } from './config.js';
|
||||
import { BALANCE } from './balance.js';
|
||||
import { createRunSeed, generateBlockedCells } from './mapGen.js';
|
||||
import { randomBetween, key, inGrid } from './utils.js';
|
||||
|
||||
export function newTurnStats() {
|
||||
return {
|
||||
|
|
@ -25,10 +27,13 @@ export function newTurnStats() {
|
|||
truckPoopFine: 0,
|
||||
maleTruckFine: 0,
|
||||
explosionDamage: 0,
|
||||
shredderBonus: 0,
|
||||
correct: 0,
|
||||
mistakes: 0,
|
||||
contractBonus: 0,
|
||||
contractResult: null
|
||||
contractResult: null,
|
||||
conveyorPasses: 0,
|
||||
eggProduced: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +52,7 @@ export function newTotalStats() {
|
|||
poopTrash: 0,
|
||||
poopMixer: 0,
|
||||
explosionDamage: 0,
|
||||
shredderBonus: 0,
|
||||
contractBonus: 0,
|
||||
zundaTax: 0,
|
||||
fairiesTribute: 0,
|
||||
|
|
@ -55,7 +61,9 @@ export function newTotalStats() {
|
|||
truckPoopFine: 0,
|
||||
maleTruckFine: 0,
|
||||
contractSuccess: 0,
|
||||
contractFailed: 0
|
||||
contractFailed: 0,
|
||||
conveyorPasses: 0,
|
||||
eggProduced: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -67,8 +75,13 @@ export function createGame() {
|
|||
timeLeft: TURN_SECONDS,
|
||||
shutdownTimeLeft: 0,
|
||||
shutdownGraceSeconds: FARM_SHUTDOWN_GRACE_SECONDS,
|
||||
cleanupTimer: null,
|
||||
cleanupBlown: false,
|
||||
lastTimestamp: 0,
|
||||
nextId: 10,
|
||||
nextEventId: 1,
|
||||
runSeed: createRunSeed(),
|
||||
blockedCells: new Set(),
|
||||
buildTool: null,
|
||||
buildSession: 0,
|
||||
undoStack: [],
|
||||
|
|
@ -80,6 +93,8 @@ export function createGame() {
|
|||
pan: null,
|
||||
view: { x: 0, y: 0 },
|
||||
debug: false,
|
||||
debugInfiniteCash: false,
|
||||
hover: null,
|
||||
chicks: [],
|
||||
effects: [],
|
||||
floatingTexts: [],
|
||||
|
|
@ -91,6 +106,10 @@ export function createGame() {
|
|||
scanners: [],
|
||||
facilities: {},
|
||||
branchCounters: new Map(),
|
||||
routingVersion: 0,
|
||||
factoryGraph: null,
|
||||
factoryGraphCommittedVersion: -1,
|
||||
factoryGraphSnapshot: null,
|
||||
mixerHalfTimer: 0,
|
||||
congestion: new Map(),
|
||||
lastExplodedComponent: new Map(),
|
||||
|
|
@ -99,15 +118,16 @@ export function createGame() {
|
|||
cardEffects: { bearing: 0, legalWork: 0, flattery: 0 },
|
||||
cardDraft: { pending: false, choices: [], rerolls: 0, picksRemaining: 0 },
|
||||
cardTargetPick: null,
|
||||
repairman: { hiredForNextDay: false, active: false, x: GRID.x + GRID.cell * 0.5, y: GRID.y + GRID.rows * GRID.cell + 84, target: null, repairedToday: 0 },
|
||||
stats: newTurnStats(),
|
||||
lastResult: null,
|
||||
eventLog: [],
|
||||
totals: newTotalStats()
|
||||
};
|
||||
}
|
||||
|
||||
export function getSpawnRange(farm) {
|
||||
const ranges = [[1.95, 4.35], [1.55, 3.45], [1.25, 2.80], [1.05, 2.35]];
|
||||
return ranges[(farm?.level || 1) - 1] || ranges[0];
|
||||
return EGG_SPAWN_RANGES[(farm?.level || 1) - 1] || EGG_SPAWN_RANGES[EGG_SPAWN_RANGES.length - 1] || [2, 4];
|
||||
}
|
||||
|
||||
export function nextSpawnDelay(farm) {
|
||||
|
|
@ -138,34 +158,107 @@ export function defaultFacilities() {
|
|||
};
|
||||
}
|
||||
|
||||
const INITIAL_CONVEYOR_BACKBONE = Object.freeze([
|
||||
// Egg Farm -> S1 top input. S1 starts lower so the farm is not pressed against it.
|
||||
[4, 1], [4, 2], [4, 3], [4, 4],
|
||||
// S1 left output -> Mixer receiver on the left grid edge.
|
||||
[3, 5], [2, 5], [1, 5], [0, 5],
|
||||
// S1 right output -> S2 top input.
|
||||
[5, 5], [6, 5], [7, 5], [8, 5], [9, 5], [10, 5], [11, 5],
|
||||
[11, 6], [11, 7],
|
||||
// S2 left output -> Waste Shredder receiver on bottom grid edge.
|
||||
[10, 8], [10, 9], [10, 10], [10, 11], [10, 12],
|
||||
// S2 right output -> Truck receiver on the bottom grid edge.
|
||||
[12, 8], [13, 8], [14, 8], [15, 8], [16, 8], [17, 8], [17, 9], [17, 10], [17, 11], [17, 12]
|
||||
]);
|
||||
|
||||
const INITIAL_SCANNERS = Object.freeze([
|
||||
{ type: 'scanner', id: 1, kind: 'manual', slot: 0, role: 0, col: 4, row: 5, level: 1, queue: [], cooldown: 0, price: BUILD_COSTS.manualScanner, builtSession: null, autoMode: 'standard' },
|
||||
{ type: 'scanner', id: 2, kind: 'manual', slot: 1, role: 1, col: 11, row: 8, level: 1, queue: [], cooldown: 0, price: BUILD_COSTS.manualScanner, builtSession: null, autoMode: 'standard' }
|
||||
]);
|
||||
|
||||
const INITIAL_EGG_FARMS = Object.freeze([
|
||||
{ type: 'eggFarm', id: 1, col: 4, row: 0, level: 1, nextSpawn: 2, lastInterval: 2, spawnCounter: 0, price: BUILD_COSTS.eggFarm, builtSession: null }
|
||||
]);
|
||||
|
||||
function addConveyorTile(game, col, row, options = {}) {
|
||||
if (!inGrid(col, row)) return;
|
||||
const k = key(col, row);
|
||||
game.conveyorTiles.add(k);
|
||||
if (!game.conveyorMeta.has(k) || options.forceMeta) {
|
||||
game.conveyorMeta.set(k, {
|
||||
price: BUILD_COSTS.conveyor,
|
||||
builtSession: null,
|
||||
uses: 0,
|
||||
durability: BALANCE.maintenance.durability.conveyor,
|
||||
maintenanceType: 'conveyor'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function scannerPortCells(scanner) {
|
||||
return [
|
||||
{ col: scanner.col, row: scanner.row - 1 },
|
||||
{ col: scanner.col - 1, row: scanner.row },
|
||||
{ col: scanner.col + 1, row: scanner.row }
|
||||
].filter(p => inGrid(p.col, p.row));
|
||||
}
|
||||
|
||||
function farmOutputCells(farm) {
|
||||
return [
|
||||
{ col: farm.col, row: farm.row + 1 },
|
||||
{ col: farm.col - 1, row: farm.row },
|
||||
{ col: farm.col + 1, row: farm.row },
|
||||
{ col: farm.col, row: farm.row - 1 }
|
||||
].filter(p => inGrid(p.col, p.row));
|
||||
}
|
||||
|
||||
function protectedInitialCells(game) {
|
||||
const reserved = new Set([...game.conveyorTiles]);
|
||||
const add = p => { if (p && inGrid(p.col, p.row)) reserved.add(key(p.col, p.row)); };
|
||||
for (const scanner of game.scanners) {
|
||||
add(scanner);
|
||||
for (const p of scannerPortCells(scanner)) add(p);
|
||||
}
|
||||
for (const farm of game.eggFarms) {
|
||||
add(farm);
|
||||
for (const p of farmOutputCells(farm)) add(p);
|
||||
}
|
||||
for (const f of Object.values(game.facilities)) add(f.entry);
|
||||
return reserved;
|
||||
}
|
||||
|
||||
function installInitialConveyorBackbone(game) {
|
||||
for (const [col, row] of INITIAL_CONVEYOR_BACKBONE) addConveyorTile(game, col, row);
|
||||
}
|
||||
|
||||
function scrubBlockedCellsFromInitialBackbone(game) {
|
||||
const protectedCells = protectedInitialCells(game);
|
||||
for (const k of protectedCells) game.blockedCells.delete(k);
|
||||
}
|
||||
|
||||
export function resetLayout(game) {
|
||||
game.conveyorTiles.clear();
|
||||
game.conveyorMeta.clear();
|
||||
const initial = [
|
||||
// Egg Farm -> S1 top input. S1 starts lower so the farm is not pressed against it.
|
||||
[4, 1], [4, 2], [4, 3], [4, 4],
|
||||
// S1 left output -> Mixer receiver on the left grid edge.
|
||||
[3, 5], [2, 5], [1, 5], [0, 5],
|
||||
// S1 right output -> S2 top input.
|
||||
[5, 5], [6, 5], [7, 5], [8, 5], [9, 5], [10, 5], [11, 5],
|
||||
[11, 6], [11, 7],
|
||||
// S2 left output -> Waste Shredder receiver on bottom grid edge.
|
||||
[10, 8], [10, 9], [10, 10], [10, 11], [10, 12],
|
||||
// S2 right output -> Truck receiver on the bottom grid edge.
|
||||
[12, 8], [13, 8], [14, 8], [15, 8], [16, 8], [17, 8], [17, 9], [17, 10], [17, 11], [17, 12]
|
||||
];
|
||||
for (const [col, row] of initial) {
|
||||
const k = `${col},${row}`;
|
||||
game.conveyorTiles.add(k);
|
||||
game.conveyorMeta.set(k, { price: BUILD_COSTS.conveyor, builtSession: null });
|
||||
}
|
||||
game.blockedCells = new Set();
|
||||
installInitialConveyorBackbone(game);
|
||||
game.facilities = defaultFacilities();
|
||||
game.scanners = [
|
||||
{ type: 'scanner', id: 1, kind: 'manual', slot: 0, role: 0, col: 4, row: 5, level: 1, queue: [], cooldown: 0, price: BUILD_COSTS.manualScanner, builtSession: null, autoMode: 'standard' },
|
||||
{ type: 'scanner', id: 2, kind: 'manual', slot: 1, role: 1, col: 11, row: 8, level: 1, queue: [], cooldown: 0, price: BUILD_COSTS.manualScanner, builtSession: null, autoMode: 'standard' }
|
||||
];
|
||||
const farm = { type: 'eggFarm', id: 1, col: 4, row: 0, level: 1, nextSpawn: 2, lastInterval: 2, spawnCounter: 0, price: BUILD_COSTS.eggFarm, builtSession: null };
|
||||
farm.nextSpawn = nextSpawnDelay(farm);
|
||||
farm.lastInterval = farm.nextSpawn;
|
||||
game.eggFarms = [farm];
|
||||
game.scanners = INITIAL_SCANNERS.map(scanner => ({ ...scanner, queue: [] }));
|
||||
game.eggFarms = INITIAL_EGG_FARMS.map(farm => {
|
||||
const nextSpawn = nextSpawnDelay(farm);
|
||||
return { ...farm, nextSpawn, lastInterval: nextSpawn };
|
||||
});
|
||||
|
||||
// Generate no-build cells only after the complete initial factory footprint is
|
||||
// installed. The protected footprint includes every default conveyor segment,
|
||||
// scanner body/ports, farm output, and machine receiver. This guarantees that
|
||||
// the starter line is always continuous from Egg Farm -> S1 -> S2 -> exits even
|
||||
// when roughly 20% of the map is blocked.
|
||||
const reserved = protectedInitialCells(game);
|
||||
game.blockedCells = generateBlockedCells({ seed: game.runSeed, targetRatio: BALANCE.map.blockedRatio, reserved: [...reserved] });
|
||||
scrubBlockedCellsFromInitialBackbone(game);
|
||||
installInitialConveyorBackbone(game);
|
||||
game.routingVersion = (game.routingVersion || 0) + 1;
|
||||
game.factoryGraph = null;
|
||||
game.factoryGraphCommittedVersion = -1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue