2025-04-02 22:33:18 +02:00

431 lines
12 KiB
JavaScript

/**********************************************************************
* RENDER
**********************************************************************/
function drawWorld() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw terrain first (water and grass)
drawTerrain();
drawGrid();
// resources
resources.forEach((res) => drawResource(res));
// roads
buildings.filter(b => b.buildingType === "Road").forEach(b => drawRoad(b));
// buildings
buildings.filter(b => b.buildingType === "House").forEach(b => drawHouse(b));
buildings.filter(b => b.buildingType === "Market").forEach(b => drawMarket(b));
buildings.filter(b => b.buildingType === "Hospital").forEach(b => drawHospital(b));
buildings.filter(b => b.buildingType === "School").forEach(b => drawSchool(b));
// city storage
drawCityStorage();
// citizens
citizens.forEach((cit) => drawCitizen(cit));
// animals
animals.forEach((ani) => {
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() {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
// Calculate visible area based on canvas size and scale
const visibleWidth = canvas.width / scale;
const visibleHeight = canvas.height / scale;
const startX = Math.floor((-offsetX / scale) - (visibleWidth / 2));
const startY = Math.floor((-offsetY / scale) - (visibleHeight / 2));
const endX = Math.ceil(startX + visibleWidth);
const endY = Math.ceil(startY + visibleHeight);
// Draw terrain cells
const cellSize = 10; // Size of each terrain cell
for (let x = startX; x <= endX; x += cellSize) {
for (let y = startY; y <= endY; y += cellSize) {
// Get terrain type at this position
const terrainType = getTerrainType(x, y);
// Set color based on terrain type
switch(terrainType) {
case TERRAIN_WATER:
ctx.fillStyle = "rgba(0, 100, 255, 0.5)"; // Blue for water
break;
case TERRAIN_GRASS:
ctx.fillStyle = "rgba(100, 200, 100, 0.3)"; // Green for grass
break;
case TERRAIN_SAND:
ctx.fillStyle = "rgba(240, 230, 140, 0.5)"; // Khaki for sand
break;
case TERRAIN_DIRT:
ctx.fillStyle = "rgba(139, 69, 19, 0.3)"; // Brown for dirt
break;
case TERRAIN_STONE:
ctx.fillStyle = "rgba(128, 128, 128, 0.4)"; // Gray for stone
break;
default:
ctx.fillStyle = "rgba(100, 200, 100, 0.3)"; // Default to grass
}
ctx.fillRect(x, y, cellSize, cellSize);
}
}
ctx.restore();
}
function drawGrid() {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
ctx.strokeStyle = "#ccc";
ctx.lineWidth = 1/scale;
let range = 2000;
for(let x = -range; x <= range; x += 100) {
ctx.beginPath();
ctx.moveTo(x, -range);
ctx.lineTo(x, range);
ctx.stroke();
}
for(let y = -range; y <= range; y += 100) {
ctx.beginPath();
ctx.moveTo(-range, y);
ctx.lineTo(range, y);
ctx.stroke();
}
ctx.restore();
}
function drawResource(res) {
if(res.amount <= 0) return;
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(res.type === "Tree") {
ctx.fillStyle = "#228B22";
ctx.beginPath();
ctx.arc(res.x, res.y, 10, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`Tree(${res.amount})`, res.x-20, res.y-12);
} else {
ctx.fillStyle = "#FF6347";
ctx.beginPath();
ctx.arc(res.x, res.y, 10, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`Fruit(${res.amount})`, res.x-25, res.y-12);
}
ctx.restore();
}
function drawHouse(b) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(!b.completed) {
ctx.strokeStyle = "#FF8C00";
ctx.lineWidth = 2/scale;
ctx.strokeRect(b.x-15, b.y-15, 30, 30);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`House(Bldg)`, b.x-30, b.y-25);
ctx.fillText(`Wood:${b.deliveredWood}/${b.requiredWood}`, b.x-30, b.y+30);
ctx.fillText(`Progress:${Math.floor(b.buildProgress)}%`, b.x-30, b.y+42);
} else {
ctx.fillStyle = "#DAA520";
ctx.fillRect(b.x-15, b.y-15, 30, 30);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText("House", b.x-15, b.y-20);
ctx.fillText(`Fruit:${b.storedFruit}/${b.maxFruit}`, b.x-25, b.y+32);
}
ctx.restore();
}
function drawMarket(b) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(!b.completed) {
ctx.strokeStyle = "#4682B4";
ctx.lineWidth = 2/scale;
ctx.strokeRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`Market(Bldg)`, b.x-30, b.y-25);
ctx.fillText(`Wood:${b.deliveredWood}/${b.requiredWood}`, b.x-30, b.y+30);
ctx.fillText(`Progress:${Math.floor(b.buildProgress)}%`, b.x-30, b.y+42);
} else {
ctx.fillStyle = "#4682B4";
ctx.fillRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText("Market", b.x-15, b.y-25);
ctx.fillText("💰", b.x-5, b.y+5);
}
ctx.restore();
}
function drawHospital(b) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(!b.completed) {
ctx.strokeStyle = "#FF6347";
ctx.lineWidth = 2/scale;
ctx.strokeRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`Hospital(Bldg)`, b.x-35, b.y-25);
ctx.fillText(`Wood:${b.deliveredWood}/${b.requiredWood}`, b.x-30, b.y+30);
ctx.fillText(`Progress:${Math.floor(b.buildProgress)}%`, b.x-30, b.y+42);
} else {
ctx.fillStyle = "#FF6347";
ctx.fillRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#fff";
ctx.font = "20px sans-serif";
ctx.fillText("🏥", b.x-10, b.y+5);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText("Hospital", b.x-20, b.y-25);
ctx.fillText(`Med:${b.medicine}/${b.maxMedicine}`, b.x-25, b.y+32);
}
ctx.restore();
}
function drawSchool(b) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(!b.completed) {
ctx.strokeStyle = "#9370DB";
ctx.lineWidth = 2/scale;
ctx.strokeRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText(`School(Bldg)`, b.x-30, b.y-25);
ctx.fillText(`Wood:${b.deliveredWood}/${b.requiredWood}`, b.x-30, b.y+30);
ctx.fillText(`Progress:${Math.floor(b.buildProgress)}%`, b.x-30, b.y+42);
} else {
ctx.fillStyle = "#9370DB";
ctx.fillRect(b.x-20, b.y-20, 40, 40);
ctx.fillStyle = "#fff";
ctx.font = "20px sans-serif";
ctx.fillText("📚", b.x-10, b.y+5);
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText("School", b.x-15, b.y-25);
ctx.fillText(`Know:${b.knowledge}/${b.maxKnowledge}`, b.x-30, b.y+32);
}
ctx.restore();
}
function drawRoad(b) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(!b.completed) {
ctx.setLineDash([5, 5]);
ctx.strokeStyle = "#888";
} else {
ctx.setLineDash([]);
ctx.strokeStyle = "#444";
}
ctx.lineWidth = 2/scale;
ctx.beginPath();
ctx.moveTo(b.x1, b.y1);
ctx.lineTo(b.x2, b.y2);
ctx.stroke();
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
if(!b.completed) {
ctx.fillText(`Road(Bldg) ${Math.floor(b.buildProgress)}%`, b.x, b.y-15);
ctx.fillText(`Wood:${b.deliveredWood}/${b.requiredWood}`, b.x-20, b.y+15);
} else {
ctx.fillText("Road", b.x-10, b.y-5);
}
ctx.restore();
}
function drawCityStorage() {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
// Draw city center
ctx.fillStyle = "#333";
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = "#fff";
ctx.font = "16px sans-serif";
ctx.fillText("🏛️", -8, 5);
ctx.restore();
}
function drawCitizen(c) {
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
// Different colors for different professions
if(c.profession === "Soldier") {
ctx.fillStyle = "#8B0000"; // Dark red for soldiers
} else {
ctx.fillStyle = c.color;
}
ctx.beginPath();
ctx.arc(c.x, c.y, 7, 0, Math.PI*2);
ctx.fill();
// Draw a sword for soldiers
if(c.profession === "Soldier") {
ctx.strokeStyle = "#DDD";
ctx.lineWidth = 1/scale;
ctx.beginPath();
ctx.moveTo(c.x + 5, c.y - 5);
ctx.lineTo(c.x + 12, c.y - 12);
ctx.stroke();
}
// Profession icon
let icon = "👤";
switch(c.profession) {
case "Builder": icon = "👷"; break;
case "Farmer": icon = "🌾"; break;
case "Merchant": icon = "💰"; break;
case "Doctor": icon = "💉"; break;
case "Teacher": icon = "📚"; break;
case "Soldier": icon = "⚔️"; break;
case "Planner": icon = "🏗️"; break;
}
ctx.fillStyle = "#000";
ctx.font = "10px sans-serif";
ctx.fillText(`${c.name} ${icon}`, c.x+10, c.y-2);
// Show stats
let stats = `W:${c.carryingWood} F:${c.carryingFruit} H:${Math.floor(c.hunger)} E:${Math.floor(c.energy)}`;
if (c.profession === "Doctor") {
stats += ` M:${c.carryingMedicine}`;
}
if (c.health < HEALTH_MAX) {
stats += ` ❤️:${Math.floor(c.health)}`;
}
if (c.education > 0) {
stats += ` 📖:${Math.floor(c.education)}`;
}
ctx.fillText(stats, c.x+10, c.y+10);
ctx.restore();
}
function drawAnimal(a) {
if(a.dead) return;
ctx.save();
ctx.translate(canvas.width/2 + offsetX, canvas.height/2 + offsetY);
ctx.scale(scale, scale);
if(a.type === "Rabbit") {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.arc(a.x, a.y, 6, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "10px sans-serif";
ctx.fillText("🐰", a.x+8, a.y+3);
} else if(a.type === "Wolf") {
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.arc(a.x, a.y, 10, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px sans-serif";
ctx.fillText("🐺", a.x+8, a.y+3);
// Show hunger level for wolves
ctx.fillStyle = "#f00";
ctx.fillRect(a.x-10, a.y-15, 20 * (a.hunger/ANIMAL_HUNGER_MAX), 3);
}
ctx.restore();
}