From c39ab3ac41d06d43c80357d37d30269db6bf4eac Mon Sep 17 00:00:00 2001 From: retoor Date: Sun, 5 Oct 2025 02:53:26 +0200 Subject: [PATCH] Update. --- static/js/GameRenderer.js | 482 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 456 insertions(+), 26 deletions(-) diff --git a/static/js/GameRenderer.js b/static/js/GameRenderer.js index 1798924..521f367 100644 --- a/static/js/GameRenderer.js +++ b/static/js/GameRenderer.js @@ -83,35 +83,50 @@ export class GameRenderer { createBuilding(buildingData) { const { type, x, y, owner_id, name } = buildingData; - // Get building height and color based on type - let height = 1; - let color = 0x808080; + // Get player color for accents + const playerColor = this.getPlayerColor(owner_id); + + // Create building group + const buildingGroup = new THREE.Group(); + buildingGroup.position.set(x * this.TILE_SIZE, 0, y * this.TILE_SIZE); + buildingGroup.userData = { x, y, owner_id, type, name }; + + // Create building based on type + let buildingMesh; if (type.includes('house')) { - height = type === 'small_house' ? - 2 : type === 'medium_house' ? 3 : 4; - color = 0xD2691E; + buildingMesh = this.createHouse(type, playerColor); } else if (type.includes('shop') || type === 'supermarket' || type === 'mall') { - height = 3; - color = 0x4169E1; + buildingMesh = this.createCommercialBuilding(type, playerColor); } else if (type.includes('factory')) { - height = 5; - color = 0x696969; + buildingMesh = this.createFactory(type, playerColor); } else if (type === 'road') { - height = 0.1; - color = 0x2F4F4F; + buildingMesh = this.createRoad(); } else if (type === 'park' || type === 'plaza') { - height = 0.5; - color = 0x32CD32; + buildingMesh = this.createPark(type, playerColor); } else if (type === 'town_hall') { - height = 6; - color = 0xFFD700; + buildingMesh = this.createTownHall(playerColor); } else if (type === 'power_plant') { - height = 8; - color = 0xFF4500; + buildingMesh = this.createPowerPlant(playerColor); + } else { + // Fallback to simple building + buildingMesh = this.createSimpleBuilding(2, 0x808080); } - // Create building mesh + buildingGroup.add(buildingMesh); + return buildingGroup; + } + + getPlayerColor(owner_id) { + // Try to find the player color from game state or use default + if (this.gameState && this.gameState.players && this.gameState.players[owner_id]) { + const color = this.gameState.players[owner_id].color; + return new THREE.Color(color); + } + return new THREE.Color(0xff6b6b); // Default reddish color + } + + createSimpleBuilding(height, color) { const geometry = new THREE.BoxGeometry( this.TILE_SIZE - 0.2, height, @@ -119,18 +134,430 @@ export class GameRenderer { ); const material = new THREE.MeshLambertMaterial({ color: color }); const building = new THREE.Mesh(geometry, material); - building.position.set( - x * this.TILE_SIZE, - height / 2, - y * this.TILE_SIZE - ); + building.position.y = height / 2; building.castShadow = true; building.receiveShadow = true; - building.userData = { x, y, owner_id, type, name }; - return building; } + createHouse(type, playerColor) { + const group = new THREE.Group(); + + // Determine house size and height + let houseWidth = 1.4; + let houseDepth = 1.2; + let wallHeight = 1.5; + let roofHeight = 0.8; + + if (type === 'medium_house') { + houseWidth = 1.6; + houseDepth = 1.4; + wallHeight = 2.0; + roofHeight = 1.0; + } else if (type === 'large_house') { + houseWidth = 1.7; + houseDepth = 1.6; + wallHeight = 2.5; + roofHeight = 1.2; + } + + // Main house body + const wallGeometry = new THREE.BoxGeometry(houseWidth, wallHeight, houseDepth); + const wallMaterial = new THREE.MeshLambertMaterial({ color: 0xf4f4f4 }); + const walls = new THREE.Mesh(wallGeometry, wallMaterial); + walls.position.y = wallHeight / 2; + walls.castShadow = true; + walls.receiveShadow = true; + group.add(walls); + + // Roof (triangular prism) - player color + const roofGeometry = new THREE.CylinderGeometry(0, houseWidth * 0.8, roofHeight, 4); + const roofMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const roof = new THREE.Mesh(roofGeometry, roofMaterial); + roof.position.y = wallHeight + roofHeight / 2; + roof.rotation.y = Math.PI / 4; // Rotate 45 degrees to make it diamond-shaped + roof.castShadow = true; + group.add(roof); + + // Door + const doorGeometry = new THREE.BoxGeometry(0.3, 0.6, 0.05); + const doorMaterial = new THREE.MeshLambertMaterial({ color: 0x8b4513 }); + const door = new THREE.Mesh(doorGeometry, doorMaterial); + door.position.set(0, 0.3, houseDepth / 2 + 0.02); + group.add(door); + + // Windows with player color frames + const createWindow = (x, y, z) => { + const windowGroup = new THREE.Group(); + + // Window frame - player color + const frameGeometry = new THREE.BoxGeometry(0.35, 0.35, 0.05); + const frameMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const frame = new THREE.Mesh(frameGeometry, frameMaterial); + windowGroup.add(frame); + + // Window glass + const glassGeometry = new THREE.BoxGeometry(0.25, 0.25, 0.02); + const glassMaterial = new THREE.MeshLambertMaterial({ + color: 0x87ceeb, + transparent: true, + opacity: 0.7 + }); + const glass = new THREE.Mesh(glassGeometry, glassMaterial); + glass.position.z = 0.01; + windowGroup.add(glass); + + windowGroup.position.set(x, y, z); + return windowGroup; + }; + + // Add windows + group.add(createWindow(-houseWidth/3, wallHeight/2, houseDepth/2 + 0.02)); + group.add(createWindow(houseWidth/3, wallHeight/2, houseDepth/2 + 0.02)); + + // Chimney for medium and large houses + if (type !== 'small_house') { + const chimneyGeometry = new THREE.BoxGeometry(0.2, 0.6, 0.2); + const chimneyMaterial = new THREE.MeshLambertMaterial({ color: 0x696969 }); + const chimney = new THREE.Mesh(chimneyGeometry, chimneyMaterial); + chimney.position.set(houseWidth/3, wallHeight + roofHeight/2 + 0.3, -houseDepth/4); + chimney.castShadow = true; + group.add(chimney); + } + + return group; + } + + createCommercialBuilding(type, playerColor) { + const group = new THREE.Group(); + + let width = 1.6; + let height = 2.5; + let depth = 1.4; + let signColor = playerColor; + + if (type === 'supermarket') { + width = 1.7; + height = 3.0; + depth = 1.6; + } else if (type === 'mall') { + width = 1.8; + height = 3.5; + depth = 1.7; + } + + // Main building + const buildingGeometry = new THREE.BoxGeometry(width, height, depth); + const buildingMaterial = new THREE.MeshLambertMaterial({ color: 0xe6e6e6 }); + const building = new THREE.Mesh(buildingGeometry, buildingMaterial); + building.position.y = height / 2; + building.castShadow = true; + building.receiveShadow = true; + group.add(building); + + // Storefront sign - player color + const signGeometry = new THREE.BoxGeometry(width * 0.9, 0.3, 0.05); + const signMaterial = new THREE.MeshLambertMaterial({ color: signColor }); + const sign = new THREE.Mesh(signGeometry, signMaterial); + sign.position.set(0, height * 0.8, depth / 2 + 0.02); + group.add(sign); + + // Large windows + const windowGeometry = new THREE.BoxGeometry(width * 0.7, height * 0.4, 0.05); + const windowMaterial = new THREE.MeshLambertMaterial({ + color: 0x4169e1, + transparent: true, + opacity: 0.6 + }); + const windows = new THREE.Mesh(windowGeometry, windowMaterial); + windows.position.set(0, height * 0.3, depth / 2 + 0.01); + group.add(windows); + + // Entrance door + const doorGeometry = new THREE.BoxGeometry(0.4, 0.8, 0.05); + const doorMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const door = new THREE.Mesh(doorGeometry, doorMaterial); + door.position.set(0, 0.4, depth / 2 + 0.02); + group.add(door); + + // Air conditioning units on roof for larger buildings + if (type === 'supermarket' || type === 'mall') { + const acGeometry = new THREE.BoxGeometry(0.4, 0.2, 0.3); + const acMaterial = new THREE.MeshLambertMaterial({ color: 0x555555 }); + const ac1 = new THREE.Mesh(acGeometry, acMaterial); + ac1.position.set(-width/3, height + 0.1, 0); + group.add(ac1); + + const ac2 = new THREE.Mesh(acGeometry, acMaterial); + ac2.position.set(width/3, height + 0.1, 0); + group.add(ac2); + } + + return group; + } + + createFactory(type, playerColor) { + const group = new THREE.Group(); + + let width = 1.6; + let height = 3.5; + let depth = 1.5; + + if (type === 'large_factory') { + width = 1.8; + height = 4.5; + depth = 1.7; + } + + // Main factory building + const buildingGeometry = new THREE.BoxGeometry(width, height, depth); + const buildingMaterial = new THREE.MeshLambertMaterial({ color: 0x696969 }); + const building = new THREE.Mesh(buildingGeometry, buildingMaterial); + building.position.y = height / 2; + building.castShadow = true; + building.receiveShadow = true; + group.add(building); + + // Smokestack - player color stripe + const stackHeight = height + 1.5; + const stackGeometry = new THREE.CylinderGeometry(0.1, 0.15, stackHeight); + const stackMaterial = new THREE.MeshLambertMaterial({ color: 0x404040 }); + const stack = new THREE.Mesh(stackGeometry, stackMaterial); + stack.position.set(width/3, stackHeight/2, -depth/4); + stack.castShadow = true; + group.add(stack); + + // Player color stripe on smokestack + const stripeGeometry = new THREE.CylinderGeometry(0.12, 0.16, 0.3); + const stripeMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const stripe = new THREE.Mesh(stripeGeometry, stripeMaterial); + stripe.position.set(width/3, stackHeight * 0.7, -depth/4); + group.add(stripe); + + // Factory windows + const createFactoryWindow = (x, y, z) => { + const windowGeometry = new THREE.BoxGeometry(0.3, 0.4, 0.05); + const windowMaterial = new THREE.MeshLambertMaterial({ + color: 0xffff88, + transparent: true, + opacity: 0.8 + }); + const window = new THREE.Mesh(windowGeometry, windowMaterial); + window.position.set(x, y, z); + return window; + }; + + // Add multiple windows + for (let i = -1; i <= 1; i++) { + for (let j = 1; j <= 2; j++) { + group.add(createFactoryWindow(i * width/3, j * height/3, depth/2 + 0.01)); + } + } + + // Loading dock - player color + const dockGeometry = new THREE.BoxGeometry(0.6, 0.1, 0.4); + const dockMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const dock = new THREE.Mesh(dockGeometry, dockMaterial); + dock.position.set(-width/3, 0.05, depth/2 + 0.2); + group.add(dock); + + return group; + } + + createRoad() { + const geometry = new THREE.BoxGeometry( + this.TILE_SIZE, + 0.1, + this.TILE_SIZE + ); + const material = new THREE.MeshLambertMaterial({ color: 0x2F4F4F }); + const road = new THREE.Mesh(geometry, material); + road.position.y = 0.05; + road.receiveShadow = true; + + return road; + } + + createPark(type, playerColor) { + const group = new THREE.Group(); + + // Base green area + const baseGeometry = new THREE.BoxGeometry(this.TILE_SIZE - 0.1, 0.1, this.TILE_SIZE - 0.1); + const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x32cd32 }); + const base = new THREE.Mesh(baseGeometry, baseMaterial); + base.position.y = 0.05; + base.receiveShadow = true; + group.add(base); + + if (type === 'park') { + // Add trees + const createTree = (x, z) => { + const treeGroup = new THREE.Group(); + + // Trunk + const trunkGeometry = new THREE.CylinderGeometry(0.05, 0.08, 0.5); + const trunkMaterial = new THREE.MeshLambertMaterial({ color: 0x8b4513 }); + const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); + trunk.position.y = 0.25; + trunk.castShadow = true; + treeGroup.add(trunk); + + // Leaves - player color accent + const leavesGeometry = new THREE.SphereGeometry(0.3, 8, 6); + const leavesMaterial = new THREE.MeshLambertMaterial({ + color: playerColor.clone().lerp(new THREE.Color(0x228b22), 0.7) + }); + const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); + leaves.position.y = 0.6; + leaves.castShadow = true; + treeGroup.add(leaves); + + treeGroup.position.set(x, 0, z); + return treeGroup; + }; + + group.add(createTree(-0.4, -0.4)); + group.add(createTree(0.4, 0.4)); + } else { // plaza + // Add fountain with player color accents + const fountainGeometry = new THREE.CylinderGeometry(0.3, 0.4, 0.3); + const fountainMaterial = new THREE.MeshLambertMaterial({ color: 0xd3d3d3 }); + const fountain = new THREE.Mesh(fountainGeometry, fountainMaterial); + fountain.position.y = 0.25; + fountain.castShadow = true; + group.add(fountain); + + // Water with player color tint + const waterGeometry = new THREE.CylinderGeometry(0.25, 0.25, 0.05); + const waterMaterial = new THREE.MeshLambertMaterial({ + color: playerColor.clone().lerp(new THREE.Color(0x4169e1), 0.5), + transparent: true, + opacity: 0.7 + }); + const water = new THREE.Mesh(waterGeometry, waterMaterial); + water.position.y = 0.35; + group.add(water); + } + + return group; + } + + createTownHall(playerColor) { + const group = new THREE.Group(); + + // Main building + const mainGeometry = new THREE.BoxGeometry(1.7, 4.0, 1.6); + const mainMaterial = new THREE.MeshLambertMaterial({ color: 0xf5f5dc }); + const main = new THREE.Mesh(mainGeometry, mainMaterial); + main.position.y = 2.0; + main.castShadow = true; + main.receiveShadow = true; + group.add(main); + + // Clock tower + const towerGeometry = new THREE.BoxGeometry(0.6, 1.5, 0.6); + const towerMaterial = new THREE.MeshLambertMaterial({ color: 0xe6e6e6 }); + const tower = new THREE.Mesh(towerGeometry, towerMaterial); + tower.position.y = 4.75; + tower.castShadow = true; + group.add(tower); + + // Clock face - player color + const clockGeometry = new THREE.CylinderGeometry(0.2, 0.2, 0.05); + const clockMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const clock = new THREE.Mesh(clockGeometry, clockMaterial); + clock.position.set(0, 4.5, 0.3); + clock.rotation.x = Math.PI / 2; + group.add(clock); + + // Flag pole with player color flag + const poleGeometry = new THREE.CylinderGeometry(0.02, 0.02, 1.0); + const poleMaterial = new THREE.MeshLambertMaterial({ color: 0x555555 }); + const pole = new THREE.Mesh(poleGeometry, poleMaterial); + pole.position.set(0.6, 5.5, 0); + group.add(pole); + + const flagGeometry = new THREE.PlaneGeometry(0.3, 0.2); + const flagMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const flag = new THREE.Mesh(flagGeometry, flagMaterial); + flag.position.set(0.75, 5.7, 0); + group.add(flag); + + // Steps with player color carpet + const stepsGeometry = new THREE.BoxGeometry(1.8, 0.2, 0.6); + const stepsMaterial = new THREE.MeshLambertMaterial({ color: 0xd3d3d3 }); + const steps = new THREE.Mesh(stepsGeometry, stepsMaterial); + steps.position.set(0, 0.1, 0.8); + group.add(steps); + + const carpetGeometry = new THREE.BoxGeometry(0.4, 0.01, 0.6); + const carpetMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const carpet = new THREE.Mesh(carpetGeometry, carpetMaterial); + carpet.position.set(0, 0.21, 0.8); + group.add(carpet); + + return group; + } + + createPowerPlant(playerColor) { + const group = new THREE.Group(); + + // Main building + const mainGeometry = new THREE.BoxGeometry(1.8, 3.0, 1.7); + const mainMaterial = new THREE.MeshLambertMaterial({ color: 0x555555 }); + const main = new THREE.Mesh(mainGeometry, mainMaterial); + main.position.y = 1.5; + main.castShadow = true; + main.receiveShadow = true; + group.add(main); + + // Cooling towers + const createCoolingTower = (x, z) => { + const towerGeometry = new THREE.CylinderGeometry(0.2, 0.3, 4.0); + const towerMaterial = new THREE.MeshLambertMaterial({ color: 0x888888 }); + const tower = new THREE.Mesh(towerGeometry, towerMaterial); + tower.position.set(x, 2.0, z); + tower.castShadow = true; + + // Player color stripe on tower + const stripeGeometry = new THREE.CylinderGeometry(0.22, 0.32, 0.3); + const stripeMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const stripe = new THREE.Mesh(stripeGeometry, stripeMaterial); + stripe.position.set(x, 3.0, z); + group.add(stripe); + + return tower; + }; + + group.add(createCoolingTower(-0.5, -0.3)); + group.add(createCoolingTower(0.5, -0.3)); + + // Control room with player color accents + const controlGeometry = new THREE.BoxGeometry(0.8, 1.0, 0.6); + const controlMaterial = new THREE.MeshLambertMaterial({ color: 0x777777 }); + const control = new THREE.Mesh(controlGeometry, controlMaterial); + control.position.set(0, 3.5, 0.4); + control.castShadow = true; + group.add(control); + + // Control room windows - player color frames + const windowGeometry = new THREE.BoxGeometry(0.6, 0.3, 0.05); + const windowMaterial = new THREE.MeshLambertMaterial({ color: playerColor }); + const windows = new THREE.Mesh(windowGeometry, windowMaterial); + windows.position.set(0, 3.5, 0.7); + group.add(windows); + + // Power lines + const lineGeometry = new THREE.CylinderGeometry(0.01, 0.01, 2.0); + const lineMaterial = new THREE.MeshLambertMaterial({ color: 0x000000 }); + const line = new THREE.Mesh(lineGeometry, lineMaterial); + line.position.set(0.8, 4.0, 0); + line.rotation.z = Math.PI / 6; + group.add(line); + + return group; + } + createCursor(playerId, color) { const geometry = new THREE.RingGeometry(0.5, 0.7, 16); const material = new THREE.MeshBasicMaterial({ @@ -144,6 +571,9 @@ export class GameRenderer { } updateGameState(gameState) { + // Store game state for player color access + this.gameState = gameState; + // Clear existing buildings this.buildings.forEach(mesh => this.scene.remove(mesh)); this.buildings.clear();