diff --git a/ai.js b/ai.js index a09474d..4d383f5 100644 --- a/ai.js +++ b/ai.js @@ -648,8 +648,21 @@ function deliverFruitTask(cit) { } function plantFruitTreeTask(cit) { - let px = cit.x + randInt(-50, 50); - let py = cit.y + randInt(-50, 50); + // Try to find a valid land position nearby + let px, py; + let attempts = 0; + do { + px = cit.x + randInt(-50, 50); + py = cit.y + randInt(-50, 50); + attempts++; + // Give up after too many attempts and find a new task + if (attempts > 20) { + cit.task = null; + cit.target = null; + return; + } + } while (!isValidPlacement(px, py)); + moveToward(cit, px, py, 0.4); if(distance(cit.x, cit.y, px, py) < 10) { if(cit.carryingFruit >= FRUIT_PLANT_COST) { diff --git a/events.js b/events.js index cc4ee0b..6618732 100644 --- a/events.js +++ b/events.js @@ -1,3 +1,7 @@ +// Track mouse position for placement indicator +let lastMouseWorldX = null; +let lastMouseWorldY = null; + /********************************************************************** * PAN & ZOOM **********************************************************************/ @@ -20,6 +24,13 @@ function setupPanZoom() { lastMouseX = e.clientX; lastMouseY = e.clientY; } + + // Update world coordinates for placement indicator + const rect = canvas.getBoundingClientRect(); + let cx = e.clientX - rect.left; + let cy = e.clientY - rect.top; + lastMouseWorldX = (cx - (canvas.width/2) - offsetX) / scale; + lastMouseWorldY = (cy - (canvas.height/2) - offsetY) / scale; }); canvas.addEventListener('wheel', (e) => { @@ -127,6 +138,12 @@ function setupCanvasClick() { let worldX = (cx - (canvas.width/2) - offsetX) / scale; let worldY = (cy - (canvas.height/2) - offsetY) / scale; + // Check if the placement is valid (not in water) + if (!isValidPlacement(worldX, worldY)) { + logAction("Cannot build on water! Choose a land location."); + return; + } + switch(purchaseMode) { case "House": if(money >= COST_HOUSE) { diff --git a/game.js b/game.js index 572207e..8c246e1 100644 --- a/game.js +++ b/game.js @@ -97,27 +97,53 @@ const WOLF_SPEED = 0.7; // Faster than rabbits * INIT WORLD **********************************************************************/ function initWorld() { - // Normal trees + // Normal trees - only on land for(let i=0; i<15; i++) { - resources.push(createResource("Tree", randInt(-1000,1000), randInt(-1000,1000), 100)); + let x, y; + do { + x = randInt(-1000,1000); + y = randInt(-1000,1000); + } while (!isValidPlacement(x, y)); + resources.push(createResource("Tree", x, y, 100)); } - // Fruit trees + + // Fruit trees - only on land for(let i=0; i<10; i++) { - resources.push(createResource("FruitTree", randInt(-1000,1000), randInt(-1000,1000), FRUIT_TREE_START_AMOUNT)); + let x, y; + do { + x = randInt(-1000,1000); + y = randInt(-1000,1000); + } while (!isValidPlacement(x, y)); + resources.push(createResource("FruitTree", x, y, FRUIT_TREE_START_AMOUNT)); } - // Start with 1 citizen => always a "Builder" - let c = createCitizen(randomName(), randInt(-200,200), randInt(-200,200), "Builder"); + // Start with 1 citizen => always a "Builder" - only on land + let cx, cy; + do { + cx = randInt(-200,200); + cy = randInt(-200,200); + } while (!isValidPlacement(cx, cy)); + let c = createCitizen(randomName(), cx, cy, "Builder"); citizens.push(c); logAction(`Initial Citizen joined: ${c.name} [Builder]`); - // Spawn some animals + // Spawn some animals - only on land for(let i=0; i { if(!ani.dead) drawAnimal(ani); }); + + // Draw placement indicator when in purchase mode + if (purchaseMode) { + drawPlacementIndicator(); + } +} + +// Draw a placement indicator at mouse position +function drawPlacementIndicator() { + if (!lastMouseWorldX || !lastMouseWorldY) return; + + ctx.save(); + ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY); + ctx.scale(scale, scale); + + const isValid = isValidPlacement(lastMouseWorldX, lastMouseWorldY); + + // Draw indicator circle + ctx.beginPath(); + ctx.arc(lastMouseWorldX, lastMouseWorldY, 15, 0, Math.PI*2); + ctx.strokeStyle = isValid ? "rgba(0, 255, 0, 0.7)" : "rgba(255, 0, 0, 0.7)"; + ctx.lineWidth = 2/scale; + ctx.stroke(); + + // Draw X if invalid + if (!isValid) { + ctx.beginPath(); + ctx.moveTo(lastMouseWorldX - 10, lastMouseWorldY - 10); + ctx.lineTo(lastMouseWorldX + 10, lastMouseWorldY + 10); + ctx.moveTo(lastMouseWorldX + 10, lastMouseWorldY - 10); + ctx.lineTo(lastMouseWorldX - 10, lastMouseWorldY + 10); + ctx.strokeStyle = "rgba(255, 0, 0, 0.7)"; + ctx.stroke(); + } + + ctx.restore(); } function drawTerrain() { diff --git a/terrain.js b/terrain.js index 2c860c5..e485b77 100644 --- a/terrain.js +++ b/terrain.js @@ -109,8 +109,8 @@ function generateTerrain(width, height, scale) { value /= 1.75; // Normalize // Determine terrain type based on noise value - // Increase water threshold to create more water areas - if (value < 0.0) { + // Lower water threshold to create more land areas + if (value < -0.3) { // Changed from 0.0 to -0.3 to reduce water terrain[x][y] = TERRAIN_WATER; } else { terrain[x][y] = TERRAIN_GRASS; diff --git a/utils.js b/utils.js index 774fd2a..b263aa8 100644 --- a/utils.js +++ b/utils.js @@ -90,6 +90,14 @@ function knockback(ent, tx, ty, dist) { } } +/********************************************************************** + * PLACEMENT VALIDATION + **********************************************************************/ +// Check if a position is valid for placement (not in water) +function isValidPlacement(x, y) { + return !isWater(x, y); +} + /********************************************************************** * FINDERS **********************************************************************/