Files
WebFactory/js/simulation.js
2026-01-13 18:32:28 +00:00

280 lines
9.3 KiB
JavaScript

// Game Simulation / Logic
const Simulation = {
gameTime: 0,
gameSpeed: 1,
// Manual mining state
mining: {
active: false,
x: 0,
y: 0,
progress: 0
},
// Initialize
init() {
this.gameTime = 0;
this.gameSpeed = 1;
this.mining = { active: false, x: 0, y: 0, progress: 0 };
},
// Main update function
update(dt) {
dt *= this.gameSpeed;
this.gameTime += dt;
this.updateManualMining(dt);
this.updateMiners(dt);
this.updateFurnaces(dt);
this.updateAssemblers(dt);
this.updateInserters(dt);
this.updateBelts(dt);
this.updateChests(dt);
},
// Manual mining update
updateManualMining(dt) {
if (!this.mining.active) return;
const tile = Terrain.getTile(this.mining.x, this.mining.y);
if (!tile || !tile.resource || tile.amount <= 0) {
this.mining.active = false;
this.mining.progress = 0;
return;
}
this.mining.progress += dt / CONFIG.MANUAL_MINE_RATE;
// Play mine sound periodically
this.mining.soundTimer = (this.mining.soundTimer || 0) + dt;
if (this.mining.soundTimer > 0.3) {
this.mining.soundTimer = 0;
Audio.playMine();
}
if (this.mining.progress >= 1) {
this.mining.progress = 0;
const result = Terrain.mine(this.mining.x, this.mining.y, CONFIG.MANUAL_MINE_AMOUNT);
if (result) {
Resources.add(result.type, result.amount);
Audio.playMineComplete();
}
// Check if resource depleted
if (!Terrain.hasResource(this.mining.x, this.mining.y)) {
this.mining.active = false;
}
}
},
// Start manual mining at position
startMining(x, y) {
if (Terrain.hasResource(x, y) && !Buildings.getAt(x, y)) {
this.mining.active = true;
this.mining.x = x;
this.mining.y = y;
this.mining.progress = 0;
return true;
}
return false;
},
// Stop manual mining
stopMining() {
this.mining.active = false;
this.mining.progress = 0;
},
// Update miners
updateMiners(dt) {
Buildings.getByType('miner').forEach(miner => {
const size = Utils.getBuildingSize('miner');
let resourceType = null;
let hasResource = false;
// Find resource under miner
for (let dy = 0; dy < size.h && !hasResource; dy++) {
for (let dx = 0; dx < size.w && !hasResource; dx++) {
const tile = Terrain.getTile(miner.x + dx, miner.y + dy);
if (tile?.resource && tile.amount > 0) {
resourceType = tile.resource;
hasResource = true;
}
}
}
if (hasResource && resourceType) {
miner.progress += dt * CONFIG.SPEEDS.miner;
if (miner.progress >= 1) {
miner.progress = 0;
Buildings.addToOutput(miner, resourceType);
// Deplete resource
for (let dy = 0; dy < size.h; dy++) {
for (let dx = 0; dx < size.w; dx++) {
const result = Terrain.mine(miner.x + dx, miner.y + dy, 1);
if (result) return;
}
}
}
}
});
},
// Update furnaces
updateFurnaces(dt) {
Buildings.getByType('furnace').forEach(furnace => {
const hasCoal = Buildings.getInventoryCount(furnace, 'coal') > 0;
const hasIron = Buildings.getInventoryCount(furnace, 'iron') > 0;
const hasCopper = Buildings.getInventoryCount(furnace, 'copper') > 0;
if (hasCoal && (hasIron || hasCopper)) {
furnace.progress += dt * CONFIG.SPEEDS.furnace;
if (furnace.progress >= 1) {
furnace.progress = 0;
Buildings.removeFromInventory(furnace, 'coal');
if (hasIron) {
Buildings.removeFromInventory(furnace, 'iron');
Buildings.addToOutput(furnace, 'iron-plate');
} else if (hasCopper) {
Buildings.removeFromInventory(furnace, 'copper');
Buildings.addToOutput(furnace, 'copper-plate');
}
}
}
});
},
// Update assemblers
updateAssemblers(dt) {
Buildings.getByType('assembler').forEach(asm => {
if (!asm.recipe) return;
const recipe = CONFIG.RECIPES[asm.recipe];
if (!recipe) return;
// Check if can craft
let canCraft = true;
for (const [item, amount] of Object.entries(recipe.inputs)) {
if (Buildings.getInventoryCount(asm, item) < amount) {
canCraft = false;
break;
}
}
if (canCraft) {
asm.progress += dt * CONFIG.SPEEDS.assembler;
if (asm.progress >= 1) {
asm.progress = 0;
// Consume inputs
for (const [item, amount] of Object.entries(recipe.inputs)) {
Buildings.removeFromInventory(asm, item, amount);
}
// Produce output
Buildings.addToOutput(asm, recipe.output, recipe.outputCount);
}
}
});
},
// Update inserters
updateInserters(dt) {
Buildings.getByType('inserter').forEach(ins => {
const dir = CONFIG.DIR[ins.rotation];
const pickupX = ins.x - dir.x;
const pickupY = ins.y - dir.y;
const dropX = ins.x + dir.x;
const dropY = ins.y + dir.y;
const source = Buildings.getAt(pickupX, pickupY);
const dest = Buildings.getAt(dropX, dropY);
if (!source) return;
// Find item to transfer from source output
for (const [item, count] of Object.entries(source.output || {})) {
if (count > 0) {
ins.progress += dt * CONFIG.SPEEDS.inserter;
if (ins.progress >= 1) {
ins.progress = 0;
Buildings.removeFromOutput(source, item);
if (dest) {
// Drop into building
Buildings.addToInventory(dest, item);
} else if (source.type === 'chest') {
// Only chests can output to player inventory
Resources.add(item, 1);
}
// If no dest and not chest, item is lost
}
break;
}
}
});
},
// Update belts
updateBelts(dt) {
Buildings.getByType('belt').forEach(belt => {
const dir = CONFIG.DIR[belt.rotation];
const nextX = belt.x + dir.x;
const nextY = belt.y + dir.y;
const nextBuilding = Buildings.getAt(nextX, nextY);
// Move items from output to next building
for (const [item, count] of Object.entries(belt.output || {})) {
if (count > 0 && nextBuilding) {
Buildings.removeFromOutput(belt, item);
Buildings.addToInventory(nextBuilding, item);
}
}
// Move items through belt (inventory -> output)
belt.progress = (belt.progress || 0) + dt * CONFIG.SPEEDS.belt;
if (belt.progress >= 1) {
belt.progress = 0;
for (const [item, count] of Object.entries(belt.inventory || {})) {
if (count > 0) {
Buildings.removeFromInventory(belt, item);
Buildings.addToOutput(belt, item);
break;
}
}
}
});
},
// Update chests (move inventory to output for inserters)
updateChests(dt) {
Buildings.getByType('chest').forEach(chest => {
for (const [item, count] of Object.entries(chest.inventory)) {
if (count > 0) {
chest.output[item] = (chest.output[item] || 0) + count;
chest.inventory[item] = 0;
}
}
});
},
// Set game speed
setSpeed(speed) {
this.gameSpeed = speed;
},
// Get state for saving
getData() {
return {
gameTime: this.gameTime
};
},
// Load state
setData(data) {
this.gameTime = data.gameTime || 0;
}
};