/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Building = Container.expand(function (buildingType) { var self = Container.call(this); buildingType = buildingType || 'residential'; self.buildingType = buildingType; var assetName = buildingType + 'Building'; var buildingGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 1.0 }); var greenSpace = self.attachAsset('greenSpace', { anchorX: 0.5, anchorY: 1.0, alpha: 0.7 }); greenSpace.y = -10; var waterSystem = self.attachAsset('waterSystem', { anchorX: 0.5, anchorY: 1.0, alpha: 0.6 }); waterSystem.y = -5; waterSystem.x = 30; var powerGrid = self.attachAsset('powerGrid', { anchorX: 0.5, anchorY: 1.0, alpha: 0.8 }); powerGrid.y = -15; powerGrid.x = -30; // Realistic city parameters self.buildingHeight = 1.0; self.buildingWidth = 1.0; self.density = 0.5; self.greenCoverage = 0.3; self.energyEfficiency = 0.5; self.waterManagement = 0.5; self.accessibility = 0.5; self.populationCapacity = 100; self.economicValue = 50; self.trafficGeneration = 50; self.pollutionLevel = 30; self.noiseLevel = 40; self.trafficScore = 50; self.sustainabilityScore = 50; self.livabilityScore = 50; self.economicScore = 50; // Building type specific properties self.setBuildingTypeProperties = function () { switch (self.buildingType) { case 'residential': self.populationCapacity = 150; self.trafficGeneration = 30; self.pollutionLevel = 10; self.noiseLevel = 20; break; case 'commercial': self.populationCapacity = 50; self.trafficGeneration = 80; self.pollutionLevel = 25; self.noiseLevel = 60; self.economicValue = 80; break; case 'industrial': self.populationCapacity = 20; self.trafficGeneration = 90; self.pollutionLevel = 70; self.noiseLevel = 80; self.economicValue = 100; break; case 'office': self.populationCapacity = 200; self.trafficGeneration = 70; self.pollutionLevel = 15; self.noiseLevel = 30; self.economicValue = 90; break; } }; self.updateBuilding = function () { buildingGraphics.scaleX = self.buildingWidth; buildingGraphics.scaleY = self.buildingHeight; // Complex visual representation based on efficiency var efficiencyLevel = (self.energyEfficiency + self.waterManagement + self.accessibility) / 3; var densityColor = Math.floor(255 * (1 - self.density * 0.7)); var efficiencyBonus = Math.floor(100 * efficiencyLevel); buildingGraphics.tint = densityColor + efficiencyBonus << 16 | densityColor + efficiencyBonus << 8 | 255; greenSpace.scaleX = self.greenCoverage; greenSpace.scaleY = self.greenCoverage; greenSpace.alpha = self.greenCoverage * 0.8; waterSystem.scaleX = self.waterManagement; waterSystem.scaleY = self.waterManagement; waterSystem.alpha = self.waterManagement * 0.8; powerGrid.scaleX = self.energyEfficiency; powerGrid.scaleY = self.energyEfficiency; powerGrid.alpha = self.energyEfficiency * 0.9; self.setBuildingTypeProperties(); self.calculateScores(); }; self.calculateScores = function () { // Complex multi-factor scoring system var densityFactor = 1 - self.density * 0.6; var heightFactor = Math.min(1, 2 - self.buildingHeight); var greenFactor = self.greenCoverage; var energyFactor = self.energyEfficiency; var waterFactor = self.waterManagement; var accessFactor = self.accessibility; // Traffic score considers building type and efficiency self.trafficScore = Math.max(0, Math.min(100, 80 - self.trafficGeneration * 0.6 + greenFactor * 25 + accessFactor * 20)); // Sustainability includes multiple environmental factors self.sustainabilityScore = Math.max(0, Math.min(100, greenFactor * 30 + energyFactor * 25 + waterFactor * 20 + heightFactor * 15 + (1 - self.pollutionLevel / 100) * 10)); // Livability considers noise, pollution, green space self.livabilityScore = Math.max(0, Math.min(100, greenFactor * 35 + (1 - self.noiseLevel / 100) * 25 + (1 - self.pollutionLevel / 100) * 20 + accessFactor * 20)); // Economic score based on building type and efficiency self.economicScore = Math.max(0, Math.min(100, self.economicValue * 0.6 + energyFactor * 20 + self.density * 20)); }; self.down = function (x, y, obj) { LK.getSound('buildingPlace').play(); tween(self, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200, easing: tween.easeOut }); tween(self, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.easeIn }); }; self.setBuildingTypeProperties(); return self; }); var BuildingTypeSelector = Container.expand(function (buildingType, label) { var self = Container.call(this); self.buildingType = buildingType; self.isSelected = false; var button = self.attachAsset('buildingTypeButton', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(label, { size: 24, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.x = 0; buttonText.y = 0; self.addChild(buttonText); self.setSelected = function (selected) { self.isSelected = selected; if (selected) { button.tint = 0x3498db; buttonText.tint = 0xFFFFFF; } else { button.tint = 0x2c3e50; buttonText.tint = 0xBDC3C7; } }; self.down = function (x, y, obj) { LK.getSound('zoneChange').play(); selectedBuildingType = self.buildingType; updateBuildingTypeSelectors(); }; return self; }); var GridCell = Container.expand(function () { var self = Container.call(this); var cellGraphics = self.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.occupied = false; self.building = null; self.zoneType = null; self.down = function (x, y, obj) { if (!self.occupied && selectedBuildingType) { self.building = new Building(selectedBuildingType); self.building.x = 0; self.building.y = 0; self.addChild(self.building); self.occupied = true; self.zoneType = selectedBuildingType; buildings.push(self.building); // Apply current parameter values to new building if (heightSlider) self.building.buildingHeight = heightSlider.currentValue; if (widthSlider) self.building.buildingWidth = widthSlider.currentValue; if (densitySlider) self.building.density = densitySlider.currentValue; if (greenSlider) self.building.greenCoverage = greenSlider.currentValue; if (energySlider) self.building.energyEfficiency = energySlider.currentValue; if (waterSlider) self.building.waterManagement = waterSlider.currentValue; if (accessSlider) self.building.accessibility = accessSlider.currentValue; self.building.updateBuilding(); LK.getSound('infrastructurePlace').play(); updateCityScores(); } else if (self.occupied && self.building) { // Show building info when tapped var globalPos = self.toGlobal({ x: 0, y: 0 }); infoDisplay.x = Math.min(2100, globalPos.x + 60); infoDisplay.y = Math.max(50, globalPos.y - 150); infoDisplay.showInfo(self.building); } }; return self; }); var InfoDisplay = Container.expand(function () { var self = Container.call(this); var background = self.attachAsset('slider', { anchorX: 0, anchorY: 0, alpha: 0.95 }); background.width = 500; background.height = 400; background.tint = 0x2c3e50; var infoText = new Text2('', { size: 28, fill: 0xFFFFFF }); infoText.anchor.set(0, 0); infoText.x = 15; infoText.y = 15; self.addChild(infoText); self.showInfo = function (building) { if (building) { var info = '=== ' + building.buildingType.toUpperCase() + ' BUILDING ===\n\n'; info += 'PHYSICAL PROPERTIES:\n'; info += '• Height: ' + building.buildingHeight.toFixed(1) + ' floors\n'; info += '• Width: ' + building.buildingWidth.toFixed(1) + 'x scale\n'; info += '• Density: ' + (building.density * 100).toFixed(0) + '%\n'; info += '• Population: ' + Math.round(building.populationCapacity * building.density) + ' people\n\n'; info += 'INFRASTRUCTURE:\n'; info += '• Green Coverage: ' + (building.greenCoverage * 100).toFixed(0) + '%\n'; info += '• Energy Efficiency: ' + (building.energyEfficiency * 100).toFixed(0) + '%\n'; info += '• Water Management: ' + (building.waterManagement * 100).toFixed(0) + '%\n'; info += '• Accessibility: ' + (building.accessibility * 100).toFixed(0) + '%\n\n'; info += 'PERFORMANCE SCORES:\n'; info += '• Traffic Impact: ' + building.trafficScore.toFixed(0) + '/100\n'; info += '• Sustainability: ' + building.sustainabilityScore.toFixed(0) + '/100\n'; info += '• Livability: ' + building.livabilityScore.toFixed(0) + '/100\n'; info += '• Economic Value: ' + building.economicScore.toFixed(0) + '/100\n\n'; info += 'ENVIRONMENTAL IMPACT:\n'; info += '• Pollution Level: ' + building.pollutionLevel.toFixed(0) + '%\n'; info += '• Noise Level: ' + building.noiseLevel.toFixed(0) + '%'; infoText.setText(info); self.visible = true; } else { self.visible = false; } }; self.visible = false; return self; }); var ParameterSlider = Container.expand(function () { var self = Container.call(this); var sliderTrack = self.attachAsset('slider', { anchorX: 0, anchorY: 0.5 }); var handle = self.attachAsset('sliderHandle', { anchorX: 0.5, anchorY: 0.5 }); self.minValue = 0; self.maxValue = 1; self.currentValue = 0.5; self.onValueChange = null; self.isDragging = false; self.updateHandle = function () { var normalizedValue = (self.currentValue - self.minValue) / (self.maxValue - self.minValue); handle.x = normalizedValue * sliderTrack.width; }; self.setValue = function (value) { self.currentValue = Math.max(self.minValue, Math.min(self.maxValue, value)); self.updateHandle(); if (self.onValueChange) { self.onValueChange(self.currentValue); } }; self.down = function (x, y, obj) { self.isDragging = true; var localX = x; var normalizedValue = Math.max(0, Math.min(1, localX / sliderTrack.width)); var newValue = self.minValue + normalizedValue * (self.maxValue - self.minValue); self.setValue(newValue); LK.getSound('parameterChange').play(); }; self.updateHandle(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50, width: 2560, height: 3200 }); /**** * Game Code ****/ var buildings = []; var gridCells = []; var selectedBuildingType = 'residential'; var buildingTypeSelectors = []; var totalTrafficScore = 0; var totalSustainabilityScore = 0; var totalLivabilityScore = 0; var totalEconomicScore = 0; var cityPopulation = 0; var cityPollution = 0; var cityNoise = 0; var targetTrafficScore = 75; var targetSustainabilityScore = 80; var targetLivabilityScore = 70; var targetEconomicScore = 65; var maxCityPopulation = 5000; // Create grid - increased size for larger interactive gameplay var gridSize = 12; var cellSize = 100; var gridStartX = 2560 / 2 - gridSize * cellSize / 2; var gridStartY = 800; for (var row = 0; row < gridSize; row++) { for (var col = 0; col < gridSize; col++) { var cell = new GridCell(); cell.x = gridStartX + col * cellSize; cell.y = gridStartY + row * cellSize; gridCells.push(cell); game.addChild(cell); } } // UI Elements var titleText = new Text2('AI City Architect', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 2560 / 2; titleText.y = 80; game.addChild(titleText); var instructionText = new Text2('Select building type, then tap grid cells to place', { size: 42, fill: 0xECF0F1 }); instructionText.anchor.set(0.5, 0); instructionText.x = 2560 / 2; instructionText.y = 160; game.addChild(instructionText); // Building Type Selectors var buildingTypes = [{ type: 'residential', label: 'RESIDENTIAL' }, { type: 'commercial', label: 'COMMERCIAL' }, { type: 'industrial', label: 'INDUSTRIAL' }, { type: 'office', label: 'OFFICE' }]; for (var i = 0; i < buildingTypes.length; i++) { var selector = new BuildingTypeSelector(buildingTypes[i].type, buildingTypes[i].label); selector.x = 420 + i * 260; selector.y = 280; buildingTypeSelectors.push(selector); game.addChild(selector); } function updateBuildingTypeSelectors() { for (var i = 0; i < buildingTypeSelectors.length; i++) { buildingTypeSelectors[i].setSelected(buildingTypeSelectors[i].buildingType === selectedBuildingType); } } // Set initial selection updateBuildingTypeSelectors(); // Parameter Sliders var heightSlider = new ParameterSlider(); heightSlider.x = 150; heightSlider.y = 450; heightSlider.minValue = 0.5; heightSlider.maxValue = 2.0; heightSlider.setValue(1.0); heightSlider.onValueChange = function (value) { updateAllBuildings('buildingHeight', value); }; game.addChild(heightSlider); var heightLabel = new Text2('Height', { size: 32, fill: 0xFFFFFF }); heightLabel.anchor.set(0, 0.5); heightLabel.x = 150; heightLabel.y = 415; game.addChild(heightLabel); var widthSlider = new ParameterSlider(); widthSlider.x = 150; widthSlider.y = 560; widthSlider.minValue = 0.5; widthSlider.maxValue = 2.0; widthSlider.setValue(1.0); widthSlider.onValueChange = function (value) { updateAllBuildings('buildingWidth', value); }; game.addChild(widthSlider); var widthLabel = new Text2('Width', { size: 32, fill: 0xFFFFFF }); widthLabel.anchor.set(0, 0.5); widthLabel.x = 150; widthLabel.y = 525; game.addChild(widthLabel); var densitySlider = new ParameterSlider(); densitySlider.x = 150; densitySlider.y = 670; densitySlider.minValue = 0.1; densitySlider.maxValue = 1.0; densitySlider.setValue(0.5); densitySlider.onValueChange = function (value) { updateAllBuildings('density', value); }; game.addChild(densitySlider); var densityLabel = new Text2('Density', { size: 32, fill: 0xFFFFFF }); densityLabel.anchor.set(0, 0.5); densityLabel.x = 150; densityLabel.y = 635; game.addChild(densityLabel); var greenSlider = new ParameterSlider(); greenSlider.x = 1750; greenSlider.y = 450; greenSlider.minValue = 0.0; greenSlider.maxValue = 1.0; greenSlider.setValue(0.3); greenSlider.onValueChange = function (value) { updateAllBuildings('greenCoverage', value); }; game.addChild(greenSlider); var greenLabel = new Text2('Green Coverage', { size: 28, fill: 0xFFFFFF }); greenLabel.anchor.set(0, 0.5); greenLabel.x = 1750; greenLabel.y = 415; game.addChild(greenLabel); // Energy Efficiency Slider var energySlider = new ParameterSlider(); energySlider.x = 1750; energySlider.y = 560; energySlider.minValue = 0.1; energySlider.maxValue = 1.0; energySlider.setValue(0.5); energySlider.onValueChange = function (value) { updateAllBuildings('energyEfficiency', value); }; game.addChild(energySlider); var energyLabel = new Text2('Energy Efficiency', { size: 28, fill: 0xFFFFFF }); energyLabel.anchor.set(0, 0.5); energyLabel.x = 1750; energyLabel.y = 525; game.addChild(energyLabel); // Water Management Slider var waterSlider = new ParameterSlider(); waterSlider.x = 1750; waterSlider.y = 670; waterSlider.minValue = 0.1; waterSlider.maxValue = 1.0; waterSlider.setValue(0.5); waterSlider.onValueChange = function (value) { updateAllBuildings('waterManagement', value); }; game.addChild(waterSlider); var waterLabel = new Text2('Water Management', { size: 28, fill: 0xFFFFFF }); waterLabel.anchor.set(0, 0.5); waterLabel.x = 1750; waterLabel.y = 635; game.addChild(waterLabel); // Accessibility Slider var accessSlider = new ParameterSlider(); accessSlider.x = 1750; accessSlider.y = 780; accessSlider.minValue = 0.2; accessSlider.maxValue = 1.0; accessSlider.setValue(0.5); accessSlider.onValueChange = function (value) { updateAllBuildings('accessibility', value); }; game.addChild(accessSlider); var accessLabel = new Text2('Accessibility', { size: 28, fill: 0xFFFFFF }); accessLabel.anchor.set(0, 0.5); accessLabel.x = 1750; accessLabel.y = 745; game.addChild(accessLabel); // Score Display var trafficScoreText = new Text2('Traffic: 0/70', { size: 38, fill: 0xFFFFFF }); trafficScoreText.anchor.set(0, 0); trafficScoreText.x = 150; trafficScoreText.y = 2000; game.addChild(trafficScoreText); var sustainabilityScoreText = new Text2('Sustainability: 0/75', { size: 38, fill: 0xFFFFFF }); sustainabilityScoreText.anchor.set(0, 0); sustainabilityScoreText.x = 150; sustainabilityScoreText.y = 2060; game.addChild(sustainabilityScoreText); var livabilityScoreText = new Text2('Livability: 0/70', { size: 38, fill: 0xFFFFFF }); livabilityScoreText.anchor.set(0, 0); livabilityScoreText.x = 150; livabilityScoreText.y = 2120; game.addChild(livabilityScoreText); var economicScoreText = new Text2('Economic: 0/65', { size: 38, fill: 0xFFFFFF }); economicScoreText.anchor.set(0, 0); economicScoreText.x = 150; economicScoreText.y = 2180; game.addChild(economicScoreText); var goalText = new Text2('Goal: Reach target scores!', { size: 42, fill: 0xE74C3C }); goalText.anchor.set(0.5, 0); goalText.x = 2560 / 2; goalText.y = 2800; game.addChild(goalText); // Add interactive information display var infoDisplay = new InfoDisplay(); game.addChild(infoDisplay); // Add building counter var buildingCountText = new Text2('Buildings: 0/20', { size: 34, fill: 0xFFFFFF }); buildingCountText.anchor.set(0, 0); buildingCountText.x = 150; buildingCountText.y = 750; game.addChild(buildingCountText); // Add population counter var populationCountText = new Text2('Population: 0/1500', { size: 34, fill: 0xFFFFFF }); populationCountText.anchor.set(0, 0); populationCountText.x = 550; populationCountText.y = 750; game.addChild(populationCountText); // Add progress indicators for the 4 main targets only var trafficProgress = LK.getAsset('trafficLine', { anchorX: 0, anchorY: 0.5 }); trafficProgress.x = 1000; trafficProgress.y = 2020; trafficProgress.scaleX = 0; trafficProgress.tint = 0xE74C3C; game.addChild(trafficProgress); var sustainabilityProgress = LK.getAsset('trafficLine', { anchorX: 0, anchorY: 0.5 }); sustainabilityProgress.x = 1000; sustainabilityProgress.y = 2080; sustainabilityProgress.scaleX = 0; sustainabilityProgress.tint = 0xE74C3C; game.addChild(sustainabilityProgress); var livabilityProgress = LK.getAsset('trafficLine', { anchorX: 0, anchorY: 0.5 }); livabilityProgress.x = 1000; livabilityProgress.y = 2140; livabilityProgress.scaleX = 0; livabilityProgress.tint = 0xE74C3C; game.addChild(livabilityProgress); var economicProgress = LK.getAsset('trafficLine', { anchorX: 0, anchorY: 0.5 }); economicProgress.x = 1000; economicProgress.y = 2200; economicProgress.scaleX = 0; economicProgress.tint = 0xE74C3C; game.addChild(economicProgress); // Add informative tips var tipsText = new Text2('Tip: Tap buildings for details, balance density with green space!', { size: 32, fill: 0x95A5A6 }); tipsText.anchor.set(0.5, 0); tipsText.x = 2560 / 2; tipsText.y = 220; game.addChild(tipsText); // Add comprehensive winning conditions information var winConditionsText = new Text2('HOW TO WIN: Reach ALL target scores (Traffic: 75+, Sustainability: 80+, Livability: 70+, Economic: 65+) + Build 20+ buildings + 1500+ population', { size: 28, fill: 0xF1C40F }); winConditionsText.anchor.set(0.5, 0); winConditionsText.x = 2560 / 2; winConditionsText.y = 2850; game.addChild(winConditionsText); var strategyTipsText = new Text2('STRATEGY: Use balanced mix of building types (residential, commercial, industrial, office) • Keep green coverage high • Optimize energy & water efficiency • Balance density carefully', { size: 26, fill: 0x3498DB }); strategyTipsText.anchor.set(0.5, 0); strategyTipsText.x = 2560 / 2; strategyTipsText.y = 2900; game.addChild(strategyTipsText); var gameplayTipsText = new Text2('CONTROLS: Select building type above • Tap grid cells to build • Adjust sliders to modify all buildings • Tap buildings for detailed info • Create realistic smart city!', { size: 26, fill: 0x95A5A6 }); gameplayTipsText.anchor.set(0.5, 0); gameplayTipsText.x = 2560 / 2; gameplayTipsText.y = 2950; game.addChild(gameplayTipsText); function updateAllBuildings(property, value) { for (var i = 0; i < buildings.length; i++) { buildings[i][property] = value; buildings[i].updateBuilding(); } updateCityScores(); } function updateCityScores() { if (buildings.length === 0) { totalTrafficScore = 0; totalSustainabilityScore = 0; totalLivabilityScore = 0; totalEconomicScore = 0; cityPopulation = 0; cityPollution = 0; cityNoise = 0; } else { var trafficSum = 0; var sustainabilitySum = 0; var livabilitySum = 0; var economicSum = 0; var populationSum = 0; var pollutionSum = 0; var noiseSum = 0; for (var i = 0; i < buildings.length; i++) { var building = buildings[i]; trafficSum += building.trafficScore; sustainabilitySum += building.sustainabilityScore; livabilitySum += building.livabilityScore; economicSum += building.economicScore; populationSum += Math.round(building.populationCapacity * building.density); pollutionSum += building.pollutionLevel; noiseSum += building.noiseLevel; } totalTrafficScore = Math.round(trafficSum / buildings.length); totalSustainabilityScore = Math.round(sustainabilitySum / buildings.length); totalLivabilityScore = Math.round(livabilitySum / buildings.length); totalEconomicScore = Math.round(economicSum / buildings.length); cityPopulation = populationSum; cityPollution = Math.round(pollutionSum / buildings.length); cityNoise = Math.round(noiseSum / buildings.length); } // Update building and population counters buildingCountText.setText('Buildings: ' + buildings.length + '/20'); populationCountText.setText('Population: ' + cityPopulation + '/1500'); // Update score displays with colors and comprehensive metrics var trafficColor = totalTrafficScore >= targetTrafficScore ? 0x2ECC71 : 0xF39C12; var sustainabilityColor = totalSustainabilityScore >= targetSustainabilityScore ? 0x2ECC71 : 0xE74C3C; var livabilityTargetMet = totalLivabilityScore >= targetLivabilityScore && buildings.length >= 20; var livabilityColor = livabilityTargetMet ? 0x2ECC71 : 0xE67E22; var economicTargetMet = totalEconomicScore >= targetEconomicScore && cityPopulation >= 1500; var economicColor = economicTargetMet ? 0x2ECC71 : 0x9B59B6; trafficScoreText.setText('Traffic Efficiency: ' + totalTrafficScore + '/' + targetTrafficScore); trafficScoreText.tint = 0xFFFFFF; trafficScoreText.alpha = 1.0; sustainabilityScoreText.setText('Sustainability: ' + totalSustainabilityScore + '/' + targetSustainabilityScore); sustainabilityScoreText.tint = 0xFFFFFF; sustainabilityScoreText.alpha = 1.0; livabilityScoreText.setText('Livability: ' + totalLivabilityScore + '/' + targetLivabilityScore + ' | Buildings: ' + buildings.length + '/20'); livabilityScoreText.tint = 0xFFFFFF; livabilityScoreText.alpha = 1.0; economicScoreText.setText('Economic: ' + totalEconomicScore + '/' + targetEconomicScore + ' | Population: ' + cityPopulation + '/1500'); economicScoreText.tint = 0xFFFFFF; economicScoreText.alpha = 1.0; // Update progress bars with individual calculations for the 4 main targets only var trafficProgressValue = Math.min(1, totalTrafficScore / targetTrafficScore); trafficProgress.scaleX = trafficProgressValue; var sustainabilityProgressValue = Math.min(1, totalSustainabilityScore / targetSustainabilityScore); sustainabilityProgress.scaleX = sustainabilityProgressValue; var livabilityProgressValue = Math.min(1, totalLivabilityScore / targetLivabilityScore); livabilityProgress.scaleX = livabilityProgressValue; var economicProgressValue = Math.min(1, totalEconomicScore / targetEconomicScore); economicProgress.scaleX = economicProgressValue; // Function to calculate color transition from red to green function getProgressColor(progress) { if (progress >= 1.0) return 0x2ECC71; // Green when complete var red = Math.floor(231 * (1 - progress) + 46 * progress); // 231 -> 46 var green = Math.floor(76 * (1 - progress) + 204 * progress); // 76 -> 204 var blue = Math.floor(60 * (1 - progress) + 113 * progress); // 60 -> 113 return red << 16 | green << 8 | blue; } // Update progress bar colors with smooth transition for the 4 main targets trafficProgress.tint = getProgressColor(trafficProgressValue); sustainabilityProgress.tint = getProgressColor(sustainabilityProgressValue); livabilityProgress.tint = getProgressColor(livabilityProgressValue); economicProgress.tint = getProgressColor(economicProgressValue); // Enhanced win condition - requires balanced city development var allTargetsMet = totalTrafficScore >= targetTrafficScore && totalSustainabilityScore >= targetSustainabilityScore && totalLivabilityScore >= targetLivabilityScore && totalEconomicScore >= targetEconomicScore; if (allTargetsMet) { var totalScore = totalTrafficScore + totalSustainabilityScore + totalLivabilityScore + totalEconomicScore; LK.setScore(totalScore); if (buildings.length >= 20 && cityPopulation >= 1500) { goalText.setText('MASTERPIECE! Perfect Smart City Achieved!'); goalText.tint = 0x2ecc71; LK.showYouWin(); } } else { var progressText = 'City Progress: '; progressText += 'Traffic:' + totalTrafficScore + ' '; progressText += 'Eco:' + totalSustainabilityScore + ' '; progressText += 'Live:' + totalLivabilityScore + ' '; progressText += 'Econ:' + totalEconomicScore; goalText.setText(progressText); goalText.tint = 0xF39C12; } } var draggedSlider = null; game.move = function (x, y, obj) { if (draggedSlider && draggedSlider.isDragging) { var localPos = draggedSlider.toLocal({ x: x, y: y }); var normalizedValue = Math.max(0, Math.min(1, localPos.x / 300)); var newValue = draggedSlider.minValue + normalizedValue * (draggedSlider.maxValue - draggedSlider.minValue); draggedSlider.setValue(newValue); } }; game.down = function (x, y, obj) { draggedSlider = null; // Hide info display when clicking elsewhere var clickedOnGridCell = false; var clickedOnSelector = false; for (var i = 0; i < gridCells.length; i++) { if (obj === gridCells[i] || obj.parent && obj.parent === gridCells[i]) { clickedOnGridCell = true; break; } } for (var i = 0; i < buildingTypeSelectors.length; i++) { if (obj === buildingTypeSelectors[i] || obj.parent && obj.parent === buildingTypeSelectors[i]) { clickedOnSelector = true; break; } } if (!clickedOnGridCell && !clickedOnSelector) { infoDisplay.showInfo(null); } if (obj === heightSlider || obj.parent === heightSlider) { draggedSlider = heightSlider; draggedSlider.isDragging = true; } else if (obj === widthSlider || obj.parent === widthSlider) { draggedSlider = widthSlider; draggedSlider.isDragging = true; } else if (obj === densitySlider || obj.parent === densitySlider) { draggedSlider = densitySlider; draggedSlider.isDragging = true; } else if (obj === greenSlider || obj.parent === greenSlider) { draggedSlider = greenSlider; draggedSlider.isDragging = true; } else if (obj === energySlider || obj.parent === energySlider) { draggedSlider = energySlider; draggedSlider.isDragging = true; } else if (obj === waterSlider || obj.parent === waterSlider) { draggedSlider = waterSlider; draggedSlider.isDragging = true; } else if (obj === accessSlider || obj.parent === accessSlider) { draggedSlider = accessSlider; draggedSlider.isDragging = true; } }; game.up = function (x, y, obj) { if (draggedSlider) { draggedSlider.isDragging = false; draggedSlider = null; } }; game.update = function () { // Animate building color variations based on performance for (var i = 0; i < buildings.length; i++) { var building = buildings[i]; var combinedScore = (building.trafficScore + building.sustainabilityScore) / 2; if (combinedScore > 60) { building.alpha = 1.0; } else { building.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Building = Container.expand(function (buildingType) {
var self = Container.call(this);
buildingType = buildingType || 'residential';
self.buildingType = buildingType;
var assetName = buildingType + 'Building';
var buildingGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
var greenSpace = self.attachAsset('greenSpace', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.7
});
greenSpace.y = -10;
var waterSystem = self.attachAsset('waterSystem', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.6
});
waterSystem.y = -5;
waterSystem.x = 30;
var powerGrid = self.attachAsset('powerGrid', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.8
});
powerGrid.y = -15;
powerGrid.x = -30;
// Realistic city parameters
self.buildingHeight = 1.0;
self.buildingWidth = 1.0;
self.density = 0.5;
self.greenCoverage = 0.3;
self.energyEfficiency = 0.5;
self.waterManagement = 0.5;
self.accessibility = 0.5;
self.populationCapacity = 100;
self.economicValue = 50;
self.trafficGeneration = 50;
self.pollutionLevel = 30;
self.noiseLevel = 40;
self.trafficScore = 50;
self.sustainabilityScore = 50;
self.livabilityScore = 50;
self.economicScore = 50;
// Building type specific properties
self.setBuildingTypeProperties = function () {
switch (self.buildingType) {
case 'residential':
self.populationCapacity = 150;
self.trafficGeneration = 30;
self.pollutionLevel = 10;
self.noiseLevel = 20;
break;
case 'commercial':
self.populationCapacity = 50;
self.trafficGeneration = 80;
self.pollutionLevel = 25;
self.noiseLevel = 60;
self.economicValue = 80;
break;
case 'industrial':
self.populationCapacity = 20;
self.trafficGeneration = 90;
self.pollutionLevel = 70;
self.noiseLevel = 80;
self.economicValue = 100;
break;
case 'office':
self.populationCapacity = 200;
self.trafficGeneration = 70;
self.pollutionLevel = 15;
self.noiseLevel = 30;
self.economicValue = 90;
break;
}
};
self.updateBuilding = function () {
buildingGraphics.scaleX = self.buildingWidth;
buildingGraphics.scaleY = self.buildingHeight;
// Complex visual representation based on efficiency
var efficiencyLevel = (self.energyEfficiency + self.waterManagement + self.accessibility) / 3;
var densityColor = Math.floor(255 * (1 - self.density * 0.7));
var efficiencyBonus = Math.floor(100 * efficiencyLevel);
buildingGraphics.tint = densityColor + efficiencyBonus << 16 | densityColor + efficiencyBonus << 8 | 255;
greenSpace.scaleX = self.greenCoverage;
greenSpace.scaleY = self.greenCoverage;
greenSpace.alpha = self.greenCoverage * 0.8;
waterSystem.scaleX = self.waterManagement;
waterSystem.scaleY = self.waterManagement;
waterSystem.alpha = self.waterManagement * 0.8;
powerGrid.scaleX = self.energyEfficiency;
powerGrid.scaleY = self.energyEfficiency;
powerGrid.alpha = self.energyEfficiency * 0.9;
self.setBuildingTypeProperties();
self.calculateScores();
};
self.calculateScores = function () {
// Complex multi-factor scoring system
var densityFactor = 1 - self.density * 0.6;
var heightFactor = Math.min(1, 2 - self.buildingHeight);
var greenFactor = self.greenCoverage;
var energyFactor = self.energyEfficiency;
var waterFactor = self.waterManagement;
var accessFactor = self.accessibility;
// Traffic score considers building type and efficiency
self.trafficScore = Math.max(0, Math.min(100, 80 - self.trafficGeneration * 0.6 + greenFactor * 25 + accessFactor * 20));
// Sustainability includes multiple environmental factors
self.sustainabilityScore = Math.max(0, Math.min(100, greenFactor * 30 + energyFactor * 25 + waterFactor * 20 + heightFactor * 15 + (1 - self.pollutionLevel / 100) * 10));
// Livability considers noise, pollution, green space
self.livabilityScore = Math.max(0, Math.min(100, greenFactor * 35 + (1 - self.noiseLevel / 100) * 25 + (1 - self.pollutionLevel / 100) * 20 + accessFactor * 20));
// Economic score based on building type and efficiency
self.economicScore = Math.max(0, Math.min(100, self.economicValue * 0.6 + energyFactor * 20 + self.density * 20));
};
self.down = function (x, y, obj) {
LK.getSound('buildingPlace').play();
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
};
self.setBuildingTypeProperties();
return self;
});
var BuildingTypeSelector = Container.expand(function (buildingType, label) {
var self = Container.call(this);
self.buildingType = buildingType;
self.isSelected = false;
var button = self.attachAsset('buildingTypeButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(label, {
size: 24,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = 0;
buttonText.y = 0;
self.addChild(buttonText);
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
button.tint = 0x3498db;
buttonText.tint = 0xFFFFFF;
} else {
button.tint = 0x2c3e50;
buttonText.tint = 0xBDC3C7;
}
};
self.down = function (x, y, obj) {
LK.getSound('zoneChange').play();
selectedBuildingType = self.buildingType;
updateBuildingTypeSelectors();
};
return self;
});
var GridCell = Container.expand(function () {
var self = Container.call(this);
var cellGraphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.occupied = false;
self.building = null;
self.zoneType = null;
self.down = function (x, y, obj) {
if (!self.occupied && selectedBuildingType) {
self.building = new Building(selectedBuildingType);
self.building.x = 0;
self.building.y = 0;
self.addChild(self.building);
self.occupied = true;
self.zoneType = selectedBuildingType;
buildings.push(self.building);
// Apply current parameter values to new building
if (heightSlider) self.building.buildingHeight = heightSlider.currentValue;
if (widthSlider) self.building.buildingWidth = widthSlider.currentValue;
if (densitySlider) self.building.density = densitySlider.currentValue;
if (greenSlider) self.building.greenCoverage = greenSlider.currentValue;
if (energySlider) self.building.energyEfficiency = energySlider.currentValue;
if (waterSlider) self.building.waterManagement = waterSlider.currentValue;
if (accessSlider) self.building.accessibility = accessSlider.currentValue;
self.building.updateBuilding();
LK.getSound('infrastructurePlace').play();
updateCityScores();
} else if (self.occupied && self.building) {
// Show building info when tapped
var globalPos = self.toGlobal({
x: 0,
y: 0
});
infoDisplay.x = Math.min(2100, globalPos.x + 60);
infoDisplay.y = Math.max(50, globalPos.y - 150);
infoDisplay.showInfo(self.building);
}
};
return self;
});
var InfoDisplay = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('slider', {
anchorX: 0,
anchorY: 0,
alpha: 0.95
});
background.width = 500;
background.height = 400;
background.tint = 0x2c3e50;
var infoText = new Text2('', {
size: 28,
fill: 0xFFFFFF
});
infoText.anchor.set(0, 0);
infoText.x = 15;
infoText.y = 15;
self.addChild(infoText);
self.showInfo = function (building) {
if (building) {
var info = '=== ' + building.buildingType.toUpperCase() + ' BUILDING ===\n\n';
info += 'PHYSICAL PROPERTIES:\n';
info += '• Height: ' + building.buildingHeight.toFixed(1) + ' floors\n';
info += '• Width: ' + building.buildingWidth.toFixed(1) + 'x scale\n';
info += '• Density: ' + (building.density * 100).toFixed(0) + '%\n';
info += '• Population: ' + Math.round(building.populationCapacity * building.density) + ' people\n\n';
info += 'INFRASTRUCTURE:\n';
info += '• Green Coverage: ' + (building.greenCoverage * 100).toFixed(0) + '%\n';
info += '• Energy Efficiency: ' + (building.energyEfficiency * 100).toFixed(0) + '%\n';
info += '• Water Management: ' + (building.waterManagement * 100).toFixed(0) + '%\n';
info += '• Accessibility: ' + (building.accessibility * 100).toFixed(0) + '%\n\n';
info += 'PERFORMANCE SCORES:\n';
info += '• Traffic Impact: ' + building.trafficScore.toFixed(0) + '/100\n';
info += '• Sustainability: ' + building.sustainabilityScore.toFixed(0) + '/100\n';
info += '• Livability: ' + building.livabilityScore.toFixed(0) + '/100\n';
info += '• Economic Value: ' + building.economicScore.toFixed(0) + '/100\n\n';
info += 'ENVIRONMENTAL IMPACT:\n';
info += '• Pollution Level: ' + building.pollutionLevel.toFixed(0) + '%\n';
info += '• Noise Level: ' + building.noiseLevel.toFixed(0) + '%';
infoText.setText(info);
self.visible = true;
} else {
self.visible = false;
}
};
self.visible = false;
return self;
});
var ParameterSlider = Container.expand(function () {
var self = Container.call(this);
var sliderTrack = self.attachAsset('slider', {
anchorX: 0,
anchorY: 0.5
});
var handle = self.attachAsset('sliderHandle', {
anchorX: 0.5,
anchorY: 0.5
});
self.minValue = 0;
self.maxValue = 1;
self.currentValue = 0.5;
self.onValueChange = null;
self.isDragging = false;
self.updateHandle = function () {
var normalizedValue = (self.currentValue - self.minValue) / (self.maxValue - self.minValue);
handle.x = normalizedValue * sliderTrack.width;
};
self.setValue = function (value) {
self.currentValue = Math.max(self.minValue, Math.min(self.maxValue, value));
self.updateHandle();
if (self.onValueChange) {
self.onValueChange(self.currentValue);
}
};
self.down = function (x, y, obj) {
self.isDragging = true;
var localX = x;
var normalizedValue = Math.max(0, Math.min(1, localX / sliderTrack.width));
var newValue = self.minValue + normalizedValue * (self.maxValue - self.minValue);
self.setValue(newValue);
LK.getSound('parameterChange').play();
};
self.updateHandle();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50,
width: 2560,
height: 3200
});
/****
* Game Code
****/
var buildings = [];
var gridCells = [];
var selectedBuildingType = 'residential';
var buildingTypeSelectors = [];
var totalTrafficScore = 0;
var totalSustainabilityScore = 0;
var totalLivabilityScore = 0;
var totalEconomicScore = 0;
var cityPopulation = 0;
var cityPollution = 0;
var cityNoise = 0;
var targetTrafficScore = 75;
var targetSustainabilityScore = 80;
var targetLivabilityScore = 70;
var targetEconomicScore = 65;
var maxCityPopulation = 5000;
// Create grid - increased size for larger interactive gameplay
var gridSize = 12;
var cellSize = 100;
var gridStartX = 2560 / 2 - gridSize * cellSize / 2;
var gridStartY = 800;
for (var row = 0; row < gridSize; row++) {
for (var col = 0; col < gridSize; col++) {
var cell = new GridCell();
cell.x = gridStartX + col * cellSize;
cell.y = gridStartY + row * cellSize;
gridCells.push(cell);
game.addChild(cell);
}
}
// UI Elements
var titleText = new Text2('AI City Architect', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 2560 / 2;
titleText.y = 80;
game.addChild(titleText);
var instructionText = new Text2('Select building type, then tap grid cells to place', {
size: 42,
fill: 0xECF0F1
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 2560 / 2;
instructionText.y = 160;
game.addChild(instructionText);
// Building Type Selectors
var buildingTypes = [{
type: 'residential',
label: 'RESIDENTIAL'
}, {
type: 'commercial',
label: 'COMMERCIAL'
}, {
type: 'industrial',
label: 'INDUSTRIAL'
}, {
type: 'office',
label: 'OFFICE'
}];
for (var i = 0; i < buildingTypes.length; i++) {
var selector = new BuildingTypeSelector(buildingTypes[i].type, buildingTypes[i].label);
selector.x = 420 + i * 260;
selector.y = 280;
buildingTypeSelectors.push(selector);
game.addChild(selector);
}
function updateBuildingTypeSelectors() {
for (var i = 0; i < buildingTypeSelectors.length; i++) {
buildingTypeSelectors[i].setSelected(buildingTypeSelectors[i].buildingType === selectedBuildingType);
}
}
// Set initial selection
updateBuildingTypeSelectors();
// Parameter Sliders
var heightSlider = new ParameterSlider();
heightSlider.x = 150;
heightSlider.y = 450;
heightSlider.minValue = 0.5;
heightSlider.maxValue = 2.0;
heightSlider.setValue(1.0);
heightSlider.onValueChange = function (value) {
updateAllBuildings('buildingHeight', value);
};
game.addChild(heightSlider);
var heightLabel = new Text2('Height', {
size: 32,
fill: 0xFFFFFF
});
heightLabel.anchor.set(0, 0.5);
heightLabel.x = 150;
heightLabel.y = 415;
game.addChild(heightLabel);
var widthSlider = new ParameterSlider();
widthSlider.x = 150;
widthSlider.y = 560;
widthSlider.minValue = 0.5;
widthSlider.maxValue = 2.0;
widthSlider.setValue(1.0);
widthSlider.onValueChange = function (value) {
updateAllBuildings('buildingWidth', value);
};
game.addChild(widthSlider);
var widthLabel = new Text2('Width', {
size: 32,
fill: 0xFFFFFF
});
widthLabel.anchor.set(0, 0.5);
widthLabel.x = 150;
widthLabel.y = 525;
game.addChild(widthLabel);
var densitySlider = new ParameterSlider();
densitySlider.x = 150;
densitySlider.y = 670;
densitySlider.minValue = 0.1;
densitySlider.maxValue = 1.0;
densitySlider.setValue(0.5);
densitySlider.onValueChange = function (value) {
updateAllBuildings('density', value);
};
game.addChild(densitySlider);
var densityLabel = new Text2('Density', {
size: 32,
fill: 0xFFFFFF
});
densityLabel.anchor.set(0, 0.5);
densityLabel.x = 150;
densityLabel.y = 635;
game.addChild(densityLabel);
var greenSlider = new ParameterSlider();
greenSlider.x = 1750;
greenSlider.y = 450;
greenSlider.minValue = 0.0;
greenSlider.maxValue = 1.0;
greenSlider.setValue(0.3);
greenSlider.onValueChange = function (value) {
updateAllBuildings('greenCoverage', value);
};
game.addChild(greenSlider);
var greenLabel = new Text2('Green Coverage', {
size: 28,
fill: 0xFFFFFF
});
greenLabel.anchor.set(0, 0.5);
greenLabel.x = 1750;
greenLabel.y = 415;
game.addChild(greenLabel);
// Energy Efficiency Slider
var energySlider = new ParameterSlider();
energySlider.x = 1750;
energySlider.y = 560;
energySlider.minValue = 0.1;
energySlider.maxValue = 1.0;
energySlider.setValue(0.5);
energySlider.onValueChange = function (value) {
updateAllBuildings('energyEfficiency', value);
};
game.addChild(energySlider);
var energyLabel = new Text2('Energy Efficiency', {
size: 28,
fill: 0xFFFFFF
});
energyLabel.anchor.set(0, 0.5);
energyLabel.x = 1750;
energyLabel.y = 525;
game.addChild(energyLabel);
// Water Management Slider
var waterSlider = new ParameterSlider();
waterSlider.x = 1750;
waterSlider.y = 670;
waterSlider.minValue = 0.1;
waterSlider.maxValue = 1.0;
waterSlider.setValue(0.5);
waterSlider.onValueChange = function (value) {
updateAllBuildings('waterManagement', value);
};
game.addChild(waterSlider);
var waterLabel = new Text2('Water Management', {
size: 28,
fill: 0xFFFFFF
});
waterLabel.anchor.set(0, 0.5);
waterLabel.x = 1750;
waterLabel.y = 635;
game.addChild(waterLabel);
// Accessibility Slider
var accessSlider = new ParameterSlider();
accessSlider.x = 1750;
accessSlider.y = 780;
accessSlider.minValue = 0.2;
accessSlider.maxValue = 1.0;
accessSlider.setValue(0.5);
accessSlider.onValueChange = function (value) {
updateAllBuildings('accessibility', value);
};
game.addChild(accessSlider);
var accessLabel = new Text2('Accessibility', {
size: 28,
fill: 0xFFFFFF
});
accessLabel.anchor.set(0, 0.5);
accessLabel.x = 1750;
accessLabel.y = 745;
game.addChild(accessLabel);
// Score Display
var trafficScoreText = new Text2('Traffic: 0/70', {
size: 38,
fill: 0xFFFFFF
});
trafficScoreText.anchor.set(0, 0);
trafficScoreText.x = 150;
trafficScoreText.y = 2000;
game.addChild(trafficScoreText);
var sustainabilityScoreText = new Text2('Sustainability: 0/75', {
size: 38,
fill: 0xFFFFFF
});
sustainabilityScoreText.anchor.set(0, 0);
sustainabilityScoreText.x = 150;
sustainabilityScoreText.y = 2060;
game.addChild(sustainabilityScoreText);
var livabilityScoreText = new Text2('Livability: 0/70', {
size: 38,
fill: 0xFFFFFF
});
livabilityScoreText.anchor.set(0, 0);
livabilityScoreText.x = 150;
livabilityScoreText.y = 2120;
game.addChild(livabilityScoreText);
var economicScoreText = new Text2('Economic: 0/65', {
size: 38,
fill: 0xFFFFFF
});
economicScoreText.anchor.set(0, 0);
economicScoreText.x = 150;
economicScoreText.y = 2180;
game.addChild(economicScoreText);
var goalText = new Text2('Goal: Reach target scores!', {
size: 42,
fill: 0xE74C3C
});
goalText.anchor.set(0.5, 0);
goalText.x = 2560 / 2;
goalText.y = 2800;
game.addChild(goalText);
// Add interactive information display
var infoDisplay = new InfoDisplay();
game.addChild(infoDisplay);
// Add building counter
var buildingCountText = new Text2('Buildings: 0/20', {
size: 34,
fill: 0xFFFFFF
});
buildingCountText.anchor.set(0, 0);
buildingCountText.x = 150;
buildingCountText.y = 750;
game.addChild(buildingCountText);
// Add population counter
var populationCountText = new Text2('Population: 0/1500', {
size: 34,
fill: 0xFFFFFF
});
populationCountText.anchor.set(0, 0);
populationCountText.x = 550;
populationCountText.y = 750;
game.addChild(populationCountText);
// Add progress indicators for the 4 main targets only
var trafficProgress = LK.getAsset('trafficLine', {
anchorX: 0,
anchorY: 0.5
});
trafficProgress.x = 1000;
trafficProgress.y = 2020;
trafficProgress.scaleX = 0;
trafficProgress.tint = 0xE74C3C;
game.addChild(trafficProgress);
var sustainabilityProgress = LK.getAsset('trafficLine', {
anchorX: 0,
anchorY: 0.5
});
sustainabilityProgress.x = 1000;
sustainabilityProgress.y = 2080;
sustainabilityProgress.scaleX = 0;
sustainabilityProgress.tint = 0xE74C3C;
game.addChild(sustainabilityProgress);
var livabilityProgress = LK.getAsset('trafficLine', {
anchorX: 0,
anchorY: 0.5
});
livabilityProgress.x = 1000;
livabilityProgress.y = 2140;
livabilityProgress.scaleX = 0;
livabilityProgress.tint = 0xE74C3C;
game.addChild(livabilityProgress);
var economicProgress = LK.getAsset('trafficLine', {
anchorX: 0,
anchorY: 0.5
});
economicProgress.x = 1000;
economicProgress.y = 2200;
economicProgress.scaleX = 0;
economicProgress.tint = 0xE74C3C;
game.addChild(economicProgress);
// Add informative tips
var tipsText = new Text2('Tip: Tap buildings for details, balance density with green space!', {
size: 32,
fill: 0x95A5A6
});
tipsText.anchor.set(0.5, 0);
tipsText.x = 2560 / 2;
tipsText.y = 220;
game.addChild(tipsText);
// Add comprehensive winning conditions information
var winConditionsText = new Text2('HOW TO WIN: Reach ALL target scores (Traffic: 75+, Sustainability: 80+, Livability: 70+, Economic: 65+) + Build 20+ buildings + 1500+ population', {
size: 28,
fill: 0xF1C40F
});
winConditionsText.anchor.set(0.5, 0);
winConditionsText.x = 2560 / 2;
winConditionsText.y = 2850;
game.addChild(winConditionsText);
var strategyTipsText = new Text2('STRATEGY: Use balanced mix of building types (residential, commercial, industrial, office) • Keep green coverage high • Optimize energy & water efficiency • Balance density carefully', {
size: 26,
fill: 0x3498DB
});
strategyTipsText.anchor.set(0.5, 0);
strategyTipsText.x = 2560 / 2;
strategyTipsText.y = 2900;
game.addChild(strategyTipsText);
var gameplayTipsText = new Text2('CONTROLS: Select building type above • Tap grid cells to build • Adjust sliders to modify all buildings • Tap buildings for detailed info • Create realistic smart city!', {
size: 26,
fill: 0x95A5A6
});
gameplayTipsText.anchor.set(0.5, 0);
gameplayTipsText.x = 2560 / 2;
gameplayTipsText.y = 2950;
game.addChild(gameplayTipsText);
function updateAllBuildings(property, value) {
for (var i = 0; i < buildings.length; i++) {
buildings[i][property] = value;
buildings[i].updateBuilding();
}
updateCityScores();
}
function updateCityScores() {
if (buildings.length === 0) {
totalTrafficScore = 0;
totalSustainabilityScore = 0;
totalLivabilityScore = 0;
totalEconomicScore = 0;
cityPopulation = 0;
cityPollution = 0;
cityNoise = 0;
} else {
var trafficSum = 0;
var sustainabilitySum = 0;
var livabilitySum = 0;
var economicSum = 0;
var populationSum = 0;
var pollutionSum = 0;
var noiseSum = 0;
for (var i = 0; i < buildings.length; i++) {
var building = buildings[i];
trafficSum += building.trafficScore;
sustainabilitySum += building.sustainabilityScore;
livabilitySum += building.livabilityScore;
economicSum += building.economicScore;
populationSum += Math.round(building.populationCapacity * building.density);
pollutionSum += building.pollutionLevel;
noiseSum += building.noiseLevel;
}
totalTrafficScore = Math.round(trafficSum / buildings.length);
totalSustainabilityScore = Math.round(sustainabilitySum / buildings.length);
totalLivabilityScore = Math.round(livabilitySum / buildings.length);
totalEconomicScore = Math.round(economicSum / buildings.length);
cityPopulation = populationSum;
cityPollution = Math.round(pollutionSum / buildings.length);
cityNoise = Math.round(noiseSum / buildings.length);
}
// Update building and population counters
buildingCountText.setText('Buildings: ' + buildings.length + '/20');
populationCountText.setText('Population: ' + cityPopulation + '/1500');
// Update score displays with colors and comprehensive metrics
var trafficColor = totalTrafficScore >= targetTrafficScore ? 0x2ECC71 : 0xF39C12;
var sustainabilityColor = totalSustainabilityScore >= targetSustainabilityScore ? 0x2ECC71 : 0xE74C3C;
var livabilityTargetMet = totalLivabilityScore >= targetLivabilityScore && buildings.length >= 20;
var livabilityColor = livabilityTargetMet ? 0x2ECC71 : 0xE67E22;
var economicTargetMet = totalEconomicScore >= targetEconomicScore && cityPopulation >= 1500;
var economicColor = economicTargetMet ? 0x2ECC71 : 0x9B59B6;
trafficScoreText.setText('Traffic Efficiency: ' + totalTrafficScore + '/' + targetTrafficScore);
trafficScoreText.tint = 0xFFFFFF;
trafficScoreText.alpha = 1.0;
sustainabilityScoreText.setText('Sustainability: ' + totalSustainabilityScore + '/' + targetSustainabilityScore);
sustainabilityScoreText.tint = 0xFFFFFF;
sustainabilityScoreText.alpha = 1.0;
livabilityScoreText.setText('Livability: ' + totalLivabilityScore + '/' + targetLivabilityScore + ' | Buildings: ' + buildings.length + '/20');
livabilityScoreText.tint = 0xFFFFFF;
livabilityScoreText.alpha = 1.0;
economicScoreText.setText('Economic: ' + totalEconomicScore + '/' + targetEconomicScore + ' | Population: ' + cityPopulation + '/1500');
economicScoreText.tint = 0xFFFFFF;
economicScoreText.alpha = 1.0;
// Update progress bars with individual calculations for the 4 main targets only
var trafficProgressValue = Math.min(1, totalTrafficScore / targetTrafficScore);
trafficProgress.scaleX = trafficProgressValue;
var sustainabilityProgressValue = Math.min(1, totalSustainabilityScore / targetSustainabilityScore);
sustainabilityProgress.scaleX = sustainabilityProgressValue;
var livabilityProgressValue = Math.min(1, totalLivabilityScore / targetLivabilityScore);
livabilityProgress.scaleX = livabilityProgressValue;
var economicProgressValue = Math.min(1, totalEconomicScore / targetEconomicScore);
economicProgress.scaleX = economicProgressValue;
// Function to calculate color transition from red to green
function getProgressColor(progress) {
if (progress >= 1.0) return 0x2ECC71; // Green when complete
var red = Math.floor(231 * (1 - progress) + 46 * progress); // 231 -> 46
var green = Math.floor(76 * (1 - progress) + 204 * progress); // 76 -> 204
var blue = Math.floor(60 * (1 - progress) + 113 * progress); // 60 -> 113
return red << 16 | green << 8 | blue;
}
// Update progress bar colors with smooth transition for the 4 main targets
trafficProgress.tint = getProgressColor(trafficProgressValue);
sustainabilityProgress.tint = getProgressColor(sustainabilityProgressValue);
livabilityProgress.tint = getProgressColor(livabilityProgressValue);
economicProgress.tint = getProgressColor(economicProgressValue);
// Enhanced win condition - requires balanced city development
var allTargetsMet = totalTrafficScore >= targetTrafficScore && totalSustainabilityScore >= targetSustainabilityScore && totalLivabilityScore >= targetLivabilityScore && totalEconomicScore >= targetEconomicScore;
if (allTargetsMet) {
var totalScore = totalTrafficScore + totalSustainabilityScore + totalLivabilityScore + totalEconomicScore;
LK.setScore(totalScore);
if (buildings.length >= 20 && cityPopulation >= 1500) {
goalText.setText('MASTERPIECE! Perfect Smart City Achieved!');
goalText.tint = 0x2ecc71;
LK.showYouWin();
}
} else {
var progressText = 'City Progress: ';
progressText += 'Traffic:' + totalTrafficScore + ' ';
progressText += 'Eco:' + totalSustainabilityScore + ' ';
progressText += 'Live:' + totalLivabilityScore + ' ';
progressText += 'Econ:' + totalEconomicScore;
goalText.setText(progressText);
goalText.tint = 0xF39C12;
}
}
var draggedSlider = null;
game.move = function (x, y, obj) {
if (draggedSlider && draggedSlider.isDragging) {
var localPos = draggedSlider.toLocal({
x: x,
y: y
});
var normalizedValue = Math.max(0, Math.min(1, localPos.x / 300));
var newValue = draggedSlider.minValue + normalizedValue * (draggedSlider.maxValue - draggedSlider.minValue);
draggedSlider.setValue(newValue);
}
};
game.down = function (x, y, obj) {
draggedSlider = null;
// Hide info display when clicking elsewhere
var clickedOnGridCell = false;
var clickedOnSelector = false;
for (var i = 0; i < gridCells.length; i++) {
if (obj === gridCells[i] || obj.parent && obj.parent === gridCells[i]) {
clickedOnGridCell = true;
break;
}
}
for (var i = 0; i < buildingTypeSelectors.length; i++) {
if (obj === buildingTypeSelectors[i] || obj.parent && obj.parent === buildingTypeSelectors[i]) {
clickedOnSelector = true;
break;
}
}
if (!clickedOnGridCell && !clickedOnSelector) {
infoDisplay.showInfo(null);
}
if (obj === heightSlider || obj.parent === heightSlider) {
draggedSlider = heightSlider;
draggedSlider.isDragging = true;
} else if (obj === widthSlider || obj.parent === widthSlider) {
draggedSlider = widthSlider;
draggedSlider.isDragging = true;
} else if (obj === densitySlider || obj.parent === densitySlider) {
draggedSlider = densitySlider;
draggedSlider.isDragging = true;
} else if (obj === greenSlider || obj.parent === greenSlider) {
draggedSlider = greenSlider;
draggedSlider.isDragging = true;
} else if (obj === energySlider || obj.parent === energySlider) {
draggedSlider = energySlider;
draggedSlider.isDragging = true;
} else if (obj === waterSlider || obj.parent === waterSlider) {
draggedSlider = waterSlider;
draggedSlider.isDragging = true;
} else if (obj === accessSlider || obj.parent === accessSlider) {
draggedSlider = accessSlider;
draggedSlider.isDragging = true;
}
};
game.up = function (x, y, obj) {
if (draggedSlider) {
draggedSlider.isDragging = false;
draggedSlider = null;
}
};
game.update = function () {
// Animate building color variations based on performance
for (var i = 0; i < buildings.length; i++) {
var building = buildings[i];
var combinedScore = (building.trafficScore + building.sustainabilityScore) / 2;
if (combinedScore > 60) {
building.alpha = 1.0;
} else {
building.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.1);
}
}
};