/****
* 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);
}
}
};