Merge pull request #20 from STEMuli-Tx/wa/game-connection

Wa/game connection
pull/18/head
jc-stemuli 2025-05-06 17:50:24 +07:00 committed by GitHub
commit 567fb6ed77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
165 changed files with 10236 additions and 2018 deletions

@ -0,0 +1,3 @@
{
"godotTools.editorPath.godot4": "c:\\Users\\jason\\Downloads\\Godot_v4.4.1-stable_mono_win64\\Godot_v4.4.1-stable_mono_win64\\Godot_v4.4.1-stable_mono_win64.exe"
}

@ -0,0 +1,69 @@
/**************************************************************************/
/* godot.audio.position.worklet.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
const POST_THRESHOLD_S = 0.1;
class GodotPositionReportingProcessor extends AudioWorkletProcessor {
constructor(...args) {
super(...args);
this.lastPostTime = currentTime;
this.position = 0;
this.ended = false;
this.port.onmessage = (event) => {
if (event?.data?.type === 'ended') {
this.ended = true;
}
};
}
process(inputs, _outputs, _parameters) {
if (this.ended) {
return false;
}
if (inputs.length > 0) {
const input = inputs[0];
if (input.length > 0) {
this.position += input[0].length;
}
}
// Posting messages is expensive. Let's limit the number of posts.
if (currentTime - this.lastPostTime > POST_THRESHOLD_S) {
this.lastPostTime = currentTime;
this.port.postMessage({ type: 'position', data: this.position });
}
return true;
}
}
registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor);

@ -0,0 +1,3 @@
[gd_resource type="Resource" format=3 uid="uid://bkkc6ej2nx3r6"]
[resource]

@ -1,6 +1,7 @@
extends Node
signal population_update(count:int)
signal receive_data_from_browser(args)
var current_scene = null

@ -15,3 +15,10 @@ func set_population_count(count: int) -> void:
EventBus.population_update.emit(population)
# Print the updated population for debugging
func receive_data_from_browser(args) -> void:
# Emit the signal to notify other nodes
EventBus.receive_data_from_browser.emit(args)

Binary file not shown.

@ -62,3 +62,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/building-small-a.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -6,7 +6,7 @@
[sub_resource type="Resource" id="Resource_7c02e"]
script = ExtResource("1_dhx01")
type = 0
type = 1
target_count = 3
current_count = 0
description = "Build 3 residential buildings"
@ -62,3 +62,4 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/road-corner.glb"])
starting_structures = Array[String]([])

@ -1,14 +1,14 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=4 format=3 uid="uid://dykbopx8n3c3v"]
[gd_resource type="Resource" script_class="MissionData" load_steps=4 format=3 uid="uid://cjr36hqnmyn0x"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_l3spi"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_dhx01"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_b4llw"]
[sub_resource type="Resource" id="Resource_c06be"]
script = ExtResource("1_l3spi")
type = 3
target_count = 50
script = ExtResource("1_dhx01")
type = 4
target_count = 1
current_count = 0
description = "Reach a population of 50 citizens"
description = "Study the construction company data and determine which company is more efficient"
completed = false
[resource]
@ -16,7 +16,7 @@ script = ExtResource("2_b4llw")
id = "3"
title = "Census Planning"
description = "As your city grows, you need to interpret your two-way frequency tables. Increase your population to see relative frequencies and possible associations between resident demographics and infrastructure usage."
objectives = Array[ExtResource("1_l3spi")]([SubResource("Resource_c06be")])
objectives = Array[ExtResource("1_dhx01")]([SubResource("Resource_c06be")])
rewards = {
"cash": 500
}
@ -60,3 +60,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/store.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -0,0 +1,86 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=9 format=3 uid="uid://bh148683scgge"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_objective"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_mission"]
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://resources/patterns/road_square.tres" id="3_pattern"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="4_building_a"]
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="5_building_c"]
[sub_resource type="Resource" id="Resource_objective1"]
script = ExtResource("1_objective")
type = 3
target_count = 1
current_count = 0
description = "Build a 2x2 road square using corner and straight roads"
completed = false
pattern_rules = ExtResource("3_pattern")
[sub_resource type="Resource" id="Resource_objective2"]
script = ExtResource("1_objective")
type = 0
target_count = 2
current_count = 0
description = "Build 2 Type A houses inside the road square"
completed = false
structure = ExtResource("4_building_a")
[sub_resource type="Resource" id="Resource_objective3"]
script = ExtResource("1_objective")
type = 0
target_count = 2
current_count = 0
description = "Build 2 Type C houses outside the road square"
completed = false
structure = ExtResource("5_building_c")
[resource]
script = ExtResource("2_mission")
id = "99"
title = "Square Neighborhood"
description = "Create a well-organized neighborhood by building a square road pattern and placing different types of houses strategically. Complete this mission to unlock all building types!"
objectives = Array[ExtResource("1_objective")]([SubResource("Resource_objective1"), SubResource("Resource_objective2"), SubResource("Resource_objective3")])
rewards = {
"cash": 500,
"experience": 100
}
next_mission_id = ""
graph_path = ""
full_screen_path = ""
intro_text = "Let's create an organized neighborhood! First, we'll build a square road pattern using corner and straight roads. Then, we'll place Type A houses inside the square and Type C houses outside to create a diverse residential area. Complete this mission to unlock all building types!"
question_text = ""
correct_answer = ""
feedback_text = ""
incorrect_feedback = ""
company_data = ""
power_math_content = ""
num_of_user_inputs = 1
input_labels = Array[String]([])
companion_dialog = {
"mission_completed": {
"animation": "excited",
"duration": 6000,
"text": "Excellent work! You've created a well-organized neighborhood with a good mix of house types. You've unlocked all building types!"
},
"mission_started": {
"animation": "excited",
"duration": 6000,
"text": ["Time to build a well-organized neighborhood!", "Let's start by creating a square road pattern."]
},
"objective_completed_1": {
"animation": "happy",
"duration": 5000,
"text": ["Perfect square! The roads are perfectly aligned.", "Now we can start placing houses inside."]
},
"objective_completed_2": {
"animation": "happy",
"duration": 5000,
"text": ["The Type A houses fit perfectly inside the road square!", "Now let's add some Type C houses outside."]
},
"objective_completed_3": {
"animation": "happy",
"duration": 5000,
"text": ["Great placement of the Type C houses outside the square!", "This creates a nice variety in our neighborhood."]
}
}
unlocked_items = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres", "res://structures/building-small-c.tres", "res://structures/building-garage.tres"])
starting_structures = Array[String]([])

@ -36,3 +36,11 @@ num_of_user_inputs = 1
input_labels = Array[String]([])
companion_dialog = {}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -45,3 +45,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/road-straight-lightposts.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/road-intersection.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -0,0 +1,63 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cua0khnbyusip"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_objective"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_mission"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="3_building_a"]
[sub_resource type="Resource" id="Resource_objective1"]
script = ExtResource("1_objective")
type = 1
target_count = 3
current_count = 0
description = "Build 3 Type A houses to unlock all building types"
completed = false
structure = ExtResource("3_building_a")
[resource]
script = ExtResource("2_mission")
id = "unlock_buildings"
title = "Building Variety"
description = "Demonstrate your basic building skills to unlock access to all residential building types!"
objectives = Array[ExtResource("1_objective")]([SubResource("Resource_objective1")])
rewards = {
"cash": 300,
"experience": 50
}
next_mission_id = "99"
graph_path = ""
full_screen_path = ""
intro_text = "The city council is impressed with your development plans! Show them you can handle basic construction by building a few houses, and they'll grant you access to build all residential building types."
question_text = ""
correct_answer = ""
feedback_text = ""
incorrect_feedback = ""
company_data = ""
power_math_content = ""
num_of_user_inputs = 0
input_labels = Array[String]([])
companion_dialog = {
"mission_completed": {
"animation": "excited",
"duration": 6000,
"text": "Great job! You now have access to all residential building types!"
},
"mission_started": {
"animation": "excited",
"duration": 6000,
"text": ["Let's start building some houses!", "Show the city council what you can do!"]
},
"objective_completed_1": {
"animation": "happy",
"duration": 5000,
"text": ["Nice work with those houses!", "The city council is impressed with your building skills!"]
}
}
unlocked_items = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres", "res://structures/building-small-c.tres", "res://structures/building-garage.tres", "res://structures/road-straight.tres"])
starting_structures = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres"])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/power_plant.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/grass-trees-tall.glb", "res://models/grass.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -58,3 +58,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -45,3 +45,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://structures/store.tres"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/pavement-fountain.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -57,3 +57,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -45,3 +45,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String](["res://models/building-small-c.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -57,3 +57,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -47,3 +47,11 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -0,0 +1,72 @@
shader_type spatial;
// Parameters for customization
uniform float alpha : hint_range(0.0, 1.0) = 0.3;
uniform float progress : hint_range(0.0, 1.0) = 0.0; // Construction progress from 0 to 1
uniform vec4 fill_color : source_color = vec4(0.2, 0.5, 0.8, 0.3); // Light blue, semi-transparent
uniform vec4 outline_color : source_color = vec4(0.0, 0.6, 1.0, 1.0); // Bright blue
uniform float outline_width : hint_range(0.0, 0.1) = 0.02;
uniform float grid_scale : hint_range(1.0, 50.0) = 20.0;
uniform float grid_width : hint_range(0.0, 0.1) = 0.02;
varying vec3 vertex_pos;
varying vec3 normal;
void vertex() {
vertex_pos = VERTEX;
normal = NORMAL;
}
float grid(vec2 uv) {
vec2 grid = fract(uv * grid_scale);
float lines = step(grid_width, grid.x) * step(grid_width, grid.y);
return 1.0 - lines;
}
void fragment() {
// Calculate fill effect based on vertical position
// Map vertex Y position to [0,1] range, ensuring we cover the full model height
float height = (vertex_pos.y + 1.0) * 0.5; // First map from [-1,1] to [0,1]
height = clamp(height, 0.0, 1.0); // Ensure we stay within [0,1]
float fill_threshold = progress;
// Create the fill effect
if (height < fill_threshold) {
// Below the fill line - show the fill color
ALBEDO = fill_color.rgb;
ALPHA = fill_color.a;
// Add some emission for a glowing effect
EMISSION = fill_color.rgb * 0.5;
} else {
// Above the fill line - show outline only
float edge_distance = abs(height - fill_threshold);
if (edge_distance < outline_width) {
// Draw the outline at the fill level
ALBEDO = outline_color.rgb;
ALPHA = outline_color.a;
EMISSION = outline_color.rgb;
} else {
// Make the unfilled part transparent
ALPHA = alpha * 0.3; // More transparent above the fill line
ALBEDO = fill_color.rgb;
}
}
// Add grid pattern
vec2 uv = vec2(
dot(normalize(vertex_pos), vec3(1.0, 0.0, 0.0)),
dot(normalize(vertex_pos), vec3(0.0, 1.0, 0.0))
);
float grid_pattern = grid(uv);
EMISSION += fill_color.rgb * grid_pattern * 0.2;
// Add edge highlighting
float fresnel = pow(1.0 - dot(normalize(normal), normalize(VIEW)), 5.0);
float outline = step(1.0 - outline_width, fresnel);
EMISSION += outline_color.rgb * outline * 2.0;
// Metallic/Roughness settings for a holographic look
METALLIC = 0.8;
ROUGHNESS = 0.2;
}

@ -0,0 +1,14 @@
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://construction_preview_material"]
[ext_resource type="Shader" path="res://models/Materials/construction_preview.gdshader" id="1_preview"]
[resource]
render_priority = 1
shader = ExtResource("1_preview")
shader_parameter/alpha = 0.3
shader_parameter/progress = 0.0
shader_parameter/fill_color = Color(0.2, 0.5, 0.8, 0.3)
shader_parameter/outline_color = Color(0.0, 0.6, 1.0, 1.0)
shader_parameter/outline_width = 0.02
shader_parameter/grid_scale = 20.0
shader_parameter/grid_width = 0.02

@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -0,0 +1,36 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://crbry5s73b6rw"
path.s3tc="res://.godot/imported/colormap.png-212e5588ca846efe35817fd63dff6086.s3tc.ctex"
path.etc2="res://.godot/imported/colormap.png-212e5588ca846efe35817fd63dff6086.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://people/Textures/colormap.png"
dest_files=["res://.godot/imported/colormap.png-212e5588ca846efe35817fd63dff6086.s3tc.ctex", "res://.godot/imported/colormap.png-212e5588ca846efe35817fd63dff6086.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-female-a.glb-8487ba6376cf5e1cdfbd9f
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-female-d.glb-3e2ed64e47d2860d66162e
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-a.glb-710e338205a7c8bbb4da1374
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-b.glb-041ef7c76883b1bd91df63a5
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-c.glb-703da87cb774a37e7943c82e
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-d.glb-767a1b7c38479b0ea96a8265
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-e.glb-348ba7ccba5be173d290dd1b
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -13,8 +13,8 @@ dest_files=["res://.godot/imported/character-male-f.glb-ce98e0c15b8a33401991ceac
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@ -22,7 +22,7 @@ nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/light_baking=0
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true

@ -27,6 +27,8 @@ general/default_playback_type.web=0
SoundManager="*res://scripts/sound_manager.gd"
EventBus="*res://global/event_bus.gd"
Globals="*res://global/globals.gd"
WebSaveManager="*res://scripts/web_save_manager.gd"
JSBridge="*res://scripts/javascript_bridge.gd"
[display]
@ -93,12 +95,12 @@ camera_rotate={
}
structure_next={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":true,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
structure_previous={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":true,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
save={

@ -0,0 +1,131 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Starter Kit City Builder"
config/tags=PackedStringArray("starterkit")
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.4", "Forward Plus")
boot_splash/bg_color=Color(0.92549, 0.92549, 0.960784, 1)
boot_splash/image="res://splash-screen.png"
config/icon="res://icon.png"
[audio]
general/default_playback_type.web=0
[autoload]
SoundManager="*res://scripts/sound_manager.gd"
<<<<<<< Updated upstream
EventBus="*res://global/event_bus.gd"
Globals="*res://global/globals.gd"
=======
WebSaveManager="*res://scripts/web_save_manager.gd"
>>>>>>> Stashed changes
[display]
window/size/viewport_width=1920
window/size/viewport_height=1080
[dotnet]
project/assembly_name="Starter Kit City Builder"
[editor]
movie_writer/movie_file="D:/Godot/recording.avi"
[editor_plugins]
enabled=PackedStringArray()
[global_classes]
LearningPanel="*res://scripts/mission/learning_panel.gd"
[input]
build={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(91, 24),"global_position":Vector2(95, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
camera_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
camera_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
camera_forward={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
camera_back={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
rotate={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(213, 17),"global_position":Vector2(217, 60),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
demolish={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194308,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
camera_rotate={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":4,"position":Vector2(107, 17),"global_position":Vector2(111, 60),"factor":1.0,"button_index":3,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
structure_next={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
structure_previous={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
save={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
load={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
camera_center={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
]
}
[rendering]
textures/vram_compression/import_etc2_astc=true
anti_aliasing/quality/msaa_3d=2
debug/shapes/navigation/enable_edge_lines=false
debug/shapes/navigation/enable_edge_lines.web=false
debug/shapes/navigation/enable_geometry_face_random_color=false
debug/shapes/navigation/enable_edge_connections=false

@ -0,0 +1,131 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Starter Kit City Builder"
config/tags=PackedStringArray("starterkit")
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.4", "Forward Plus")
boot_splash/bg_color=Color(0.92549, 0.92549, 0.960784, 1)
boot_splash/image="res://splash-screen.png"
config/icon="res://icon.png"
[audio]
general/default_playback_type.web=0
[autoload]
SoundManager="*res://scripts/sound_manager.gd"
<<<<<<< Updated upstream
EventBus="*res://global/event_bus.gd"
Globals="*res://global/globals.gd"
=======
WebSaveManager="*res://scripts/web_save_manager.gd"
>>>>>>> Stashed changes
[display]
window/size/viewport_width=1920
window/size/viewport_height=1080
[dotnet]
project/assembly_name="Starter Kit City Builder"
[editor]
movie_writer/movie_file="D:/Godot/recording.avi"
[editor_plugins]
enabled=PackedStringArray()
[global_classes]
LearningPanel="*res://scripts/mission/learning_panel.gd"
[input]
build={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(91, 24),"global_position":Vector2(95, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
camera_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
camera_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
camera_forward={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
camera_back={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
rotate={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(213, 17),"global_position":Vector2(217, 60),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
demolish={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194308,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
camera_rotate={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":4,"position":Vector2(107, 17),"global_position":Vector2(111, 60),"factor":1.0,"button_index":3,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
structure_next={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
structure_previous={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
save={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
load={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
camera_center={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
]
}
[rendering]
textures/vram_compression/import_etc2_astc=true
anti_aliasing/quality/msaa_3d=2
debug/shapes/navigation/enable_edge_lines=false
debug/shapes/navigation/enable_edge_lines.web=false
debug/shapes/navigation/enable_geometry_face_random_color=false
debug/shapes/navigation/enable_edge_connections=false

@ -0,0 +1,10 @@
[gd_resource type="Resource" script_class="GenericText" load_steps=2 format=3 uid="uid://bljpugjou1cxo"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="1_resource"]
[resource]
script = ExtResource("1_resource")
panel_type = 0
title = "Building Selector"
body_text = "Select structures to build in your city."
button_text = "Close"

@ -0,0 +1,39 @@
[gd_resource type="Resource" script_class="PatternRules" load_steps=9 format=3 uid="uid://dykbopx8n3c3v"]
[ext_resource type="Script" path="res://scripts/mission/pattern_rules.gd" id="1_rules"]
[ext_resource type="Script" path="res://scripts/mission/pattern_rule.gd" id="2_rule"]
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="3_corner"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="4_straight"]
[sub_resource type="Resource" id="Resource_corner1"]
script = ExtResource("2_rule")
type = 0
offset = Vector2i(0, 0)
structure = ExtResource("3_corner")
rotation = 0
[sub_resource type="Resource" id="Resource_straight1"]
script = ExtResource("2_rule")
type = 0
offset = Vector2i(1, 0)
structure = ExtResource("4_straight")
rotation = 0
[sub_resource type="Resource" id="Resource_straight2"]
script = ExtResource("2_rule")
type = 0
offset = Vector2i(0, 1)
structure = ExtResource("4_straight")
rotation = 90
[sub_resource type="Resource" id="Resource_corner2"]
script = ExtResource("2_rule")
type = 0
offset = Vector2i(1, 1)
structure = ExtResource("3_corner")
rotation = 180
[resource]
script = ExtResource("1_rules")
pattern_size = Vector2i(2, 2)
rules = Array[ExtResource("2_rule")]([SubResource("Resource_corner1"), SubResource("Resource_straight1"), SubResource("Resource_straight2"), SubResource("Resource_corner2")])

@ -0,0 +1,223 @@
[gd_scene load_steps=4 format=3 uid="uid://b4k3xfm8pd8qw"]
[ext_resource type="Script" uid="uid://bsjmj0qu3xfrr" path="res://scripts/building_selector.gd" id="1_jybm7"]
[ext_resource type="Resource" uid="uid://bljpugjou1cxo" path="res://resources/building_selector.tres" id="2_resource"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.356863, 0.670588, 0.768627, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[node name="BuildingSelector" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
script = ExtResource("1_jybm7")
resource_data = ExtResource("2_resource")
[node name="ClickBlocker" type="ColorRect" parent="."]
visible = false
z_index = 1000
layout_mode = 2
mouse_filter = 2
color = Color(0, 0, 0, 0.01)
[node name="MainButton" type="Button" parent="."]
custom_minimum_size = Vector2(30, 30)
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
mouse_filter = 2
text = "▶"
flat = true
[node name="SelectionPanel" type="Panel" parent="."]
visible = false
layout_mode = 2
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="MarginContainer" type="MarginContainer" parent="SelectionPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 10
[node name="HBoxContainer" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
[node name="TitleLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
text = "Building Selector"
[node name="CloseButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 2
text = "✕"
flat = true
[node name="SearchBar" type="LineEdit" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
placeholder_text = "Search structures..."
[node name="FilterButtons" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 5
[node name="AllButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 2
toggle_mode = true
button_pressed = true
text = "All"
flat = true
[node name="GroundButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 2
toggle_mode = true
text = "Ground"
flat = true
[node name="BuildingButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 2
toggle_mode = true
text = "Buildings"
flat = true
[node name="ScrollContainer" type="ScrollContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
mouse_filter = 2
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_vertical = 3
mouse_filter = 2
[node name="GroundSection" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
[node name="Label" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Ground Structures"
[node name="GroundOptions" type="GridContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 4
[node name="BuildingSection" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
[node name="Label" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Building Structures"
[node name="BuildingOptions" type="GridContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 4
[node name="DescriptionPanel" type="Panel" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="MarginContainer" type="MarginContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 10
[node name="TitleLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Structure Name"
[node name="DescriptionLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Structure description goes here..."
autowrap_mode = 2
[node name="StatsContainer" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 20
[node name="PriceLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Price: $0"
[node name="PopulationLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Population: 0"
[node name="PowerLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Power: 0"
[connection signal="pressed" from="MainButton" to="." method="_on_main_button_pressed"]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer/CloseButton" to="." method="hide_panel"]
[connection signal="text_changed" from="SelectionPanel/MarginContainer/VBoxContainer/SearchBar" to="." method="_on_search_text_changed"]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/AllButton" to="." method="_on_filter_button_pressed" binds= ["All"]]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/GroundButton" to="." method="_on_filter_button_pressed" binds= ["Ground"]]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/BuildingButton" to="." method="_on_filter_button_pressed" binds= ["Buildings"]]

@ -80,7 +80,7 @@ transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0, 0, 0)
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_8ib1s")
&"": SubResource("AnimationLibrary_8ib1s")
}
autoplay = "walk"
speed_scale = 3.0

@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://c03crmaif3bss"]
[ext_resource type="Script" uid="uid://bat3h7fhpdh7" path="res://scripts/economy_manager.gd" id="1_abcde"]
[node name="EconomyManager" type="Node"]
script = ExtResource("1_abcde")
[node name="EconomyTimer" type="Timer" parent="."]
autostart = true

@ -17,10 +17,10 @@ ambient_light_source = 3
ambient_light_color = Color(0.36875, 0.741929, 0.801344, 1)
tonemap_mode = 3
ssao_enabled = true
ssao_radius = 0.25
ssao_intensity = 0.5
ssao_power = 100.0
ssao_horizon = 0.1
ssao_detail = 1.0
ssao_horizon = 0.25
ssao_light_affect = 0.5
ssao_ao_channel_affect = 0.75
sdfgi_cascades = 1
sdfgi_max_distance = 25.6
sdfgi_energy = 0.5

@ -1,4 +1,4 @@
[gd_scene load_steps=71 format=3 uid="uid://vgwrcfy1qawf"]
[gd_scene load_steps=38 format=3 uid="uid://vgwrcfy1qawf"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Environment" uid="uid://jbptgqvstei3" path="res://scenes/main-environment.tres" id="1_yndf3"]
@ -17,51 +17,20 @@
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="13_kf5aa"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://n26k1k7243dd" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://dnquivpg0ead" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Resource" uid="uid://dqqe3iofnleup" path="res://structures/pavement-fountain.tres" id="14_t5ykj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://dveu4dnue0d54" path="res://structures/road-intersection.tres" id="15_e755i"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/unit_1.02/census_planning_1.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/unit_1.02/census_planning_2.tres" id="16_rl54y"]
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://mission/unit_1.02/census_planning_3.tres" id="17_rrdy6"]
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="17_vlub6"]
[ext_resource type="PackedScene" uid="uid://b4gkfwf4i3ydl" path="res://scenes/character.tscn" id="18_8lrh8"]
[ext_resource type="PackedScene" uid="uid://cgk66f6rg03mj" path="res://scenes/hud.tscn" id="18_hud"]
[ext_resource type="PackedScene" uid="uid://bqjnp7uypupog" path="res://scenes/controls_panel.tscn" id="19_controls"]
[ext_resource type="Script" uid="uid://b5nubrdeseay4" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://442cwthak2pa" path="res://mission/unit_1.02/market_research_1.tres" id="20_ngu16"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="21_y11qv"]
[ext_resource type="PackedScene" uid="uid://btfwonjc8uj0w" path="res://scenes/mission_select_menu.tscn" id="24_ro3en"]
[ext_resource type="Resource" uid="uid://bom5bu47dy5kp" path="res://mission/unit_1.02/market_research_2.tres" id="24_xud6a"]
[ext_resource type="Resource" uid="uid://csrqvfwp63ygr" path="res://mission/unit_1.02/market_research_3.tres" id="25_6hx7u"]
[ext_resource type="Resource" uid="uid://qwiwim2pg88f" path="res://mission/unit_1.02/market_research_4.tres" id="26_lvk23"]
[ext_resource type="Resource" uid="uid://cfgw8dblm55c5" path="res://mission/unit_1.03_1.05/grid_growth_1.tres" id="27_s0e58"]
[ext_resource type="Resource" uid="uid://ba3ndftq7dht7" path="res://mission/unit_1.03_1.05/grid_growth_2.tres" id="28_hurxs"]
[ext_resource type="Resource" uid="uid://dgimr2v12rjqu" path="res://mission/unit_1.03_1.05/grid_growth_3.tres" id="29_rhn1n"]
[ext_resource type="Resource" uid="uid://dm2o4dq2oml53" path="res://mission/unit_1.03_1.05/grid_growth_4.tres" id="30_4rwkv"]
[ext_resource type="Resource" uid="uid://btwrfq37q8vey" path="res://mission/unit_1.03_1.05/traffic_flow_1.tres" id="31_j2idb"]
[ext_resource type="Resource" uid="uid://cf7gpb4j7gq1g" path="res://mission/unit_1.03_1.05/traffic_flow_2.tres" id="32_ipu0c"]
[ext_resource type="Resource" uid="uid://ddmxjjyxgxyxo" path="res://mission/unit_1.03_1.05/traffic_flow_3.tres" id="33_c0l5e"]
[ext_resource type="Resource" uid="uid://doxd30r8qbgdq" path="res://mission/unit_1.03_1.05/traffic_flow_4.tres" id="34_21t20"]
[ext_resource type="Resource" uid="uid://duaxn13myfx22" path="res://mission/unit_1.06/sustainable_dev_1.tres" id="35_o0bjh"]
[ext_resource type="Resource" uid="uid://fuxb3pfbbwjm" path="res://mission/unit_1.06/sustainable_dev_2.tres" id="36_2wodh"]
[ext_resource type="Resource" uid="uid://byd5jxiutxpky" path="res://mission/unit_1.06/sustainable_dev_3.tres" id="37_psgx1"]
[ext_resource type="Resource" uid="uid://daug1o7kppqit" path="res://mission/unit_1.06/sustainable_dev_4.tres" id="38_hw762"]
[ext_resource type="Resource" uid="uid://cp7tcpktwlrkt" path="res://mission/unit_1.06/urban_planning_1.tres" id="39_ymw5p"]
[ext_resource type="Resource" uid="uid://c3q1afcvwi4rk" path="res://mission/unit_1.06/urban_planning_2.tres" id="40_uggp1"]
[ext_resource type="Resource" uid="uid://ct1k7n2oopwdu" path="res://mission/unit_1.06/urban_planning_3.tres" id="41_f0dxf"]
[ext_resource type="Resource" uid="uid://d1fykuxfmh2q1" path="res://mission/unit_1.06/urban_planning_4.tres" id="42_fv8gl"]
[ext_resource type="Resource" uid="uid://detwnqsq87r30" path="res://mission/unit_1.07/economic_forecast_1.tres" id="43_qvne6"]
[ext_resource type="Resource" uid="uid://bj7tjuknfaeyg" path="res://mission/unit_1.07/economic_forecast_2.tres" id="44_haub2"]
[ext_resource type="Resource" uid="uid://ctyrlnq5cxuiu" path="res://mission/unit_1.07/economic_forecast_3.tres" id="45_xs8xk"]
[ext_resource type="Resource" uid="uid://bwrkqv42wk8f" path="res://mission/unit_1.07/resource_alloc_1.tres" id="47_6w4y8"]
[ext_resource type="Resource" uid="uid://d0nblitd4ixir" path="res://mission/unit_1.07/resource_alloc_2.tres" id="48_ck35a"]
[ext_resource type="Resource" uid="uid://cxh8dgf54oimx" path="res://mission/unit_1.07/resource_alloc_3.tres" id="49_cvgxw"]
[ext_resource type="Resource" uid="uid://cpfr2xnjtpcog" path="res://mission/unit_1.07/resource_alloc_4.tres" id="50_6ke0d"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="51_kf5aa"]
[ext_resource type="Resource" uid="uid://bsic030rpgh08" path="res://mission/unit_1.06/sustainable_dev_2b.tres" id="57_e755i"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="26_tel4y"]
[ext_resource type="Resource" uid="uid://cua0khnbyusip" path="res://mission/unit_1.02/unlock_buildings.tres" id="27_qkpxi"]
[sub_resource type="Resource" id="Resource_1gdbm"]
script = ExtResource("14_76jlq")
@ -96,49 +65,6 @@ body_text = "Congratulations, City Planner!
"
button_text = "Close"
[sub_resource type="Resource" id="Resource_f1uf2"]
script = ExtResource("51_kf5aa")
type = 2
target_count = 1000
current_count = 0
description = "Maintain a minimum of 1000 money"
completed = false
[sub_resource type="Resource" id="Resource_t5ykj"]
script = ExtResource("14_vcrh8")
id = "27"
title = "Economic Forecasting"
description = "Financial reserves are critical for economic stability. Use your forecasting model to ensure your city maintains a healthy treasury."
objectives = Array[ExtResource("51_kf5aa")]([SubResource("Resource_f1uf2")])
rewards = {
"cash": 500
}
next_mission_id = "28"
graph_path = ""
full_screen_path = ""
intro_text = "Let's forecast financial stability! Maintain a minimum treasury of 1000 money to ensure your city's economic health."
question_text = ""
correct_answer = ""
feedback_text = ""
incorrect_feedback = ""
company_data = ""
power_math_content = ""
num_of_user_inputs = 1
input_labels = Array[String]([])
companion_dialog = {
"mission_completed": {
"animation": "happy",
"duration": 6000,
"text": ["Well done maintaining a healthy treasury! Your financial forecasting model shows strong fiscal health."]
},
"mission_started": {
"animation": "excited",
"duration": 6000,
"text": ["Time to focus on fiscal forecasting! A strong treasury indicates good economic management."]
}
}
unlocked_items = Array[String]([])
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mission"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
border_width_left = 2
@ -253,7 +179,7 @@ visible = false
[node name="MissionManager" type="Node" parent="." node_paths=PackedStringArray("mission_ui", "builder")]
script = ExtResource("10_oe3re")
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("17_rrdy6"), ExtResource("20_ngu16"), ExtResource("24_xud6a"), ExtResource("25_6hx7u"), ExtResource("26_lvk23"), ExtResource("27_s0e58"), ExtResource("28_hurxs"), ExtResource("29_rhn1n"), ExtResource("30_4rwkv"), ExtResource("31_j2idb"), ExtResource("32_ipu0c"), ExtResource("33_c0l5e"), ExtResource("34_21t20"), ExtResource("35_o0bjh"), ExtResource("36_2wodh"), ExtResource("37_psgx1"), ExtResource("38_hw762"), ExtResource("39_ymw5p"), ExtResource("40_uggp1"), ExtResource("41_f0dxf"), ExtResource("42_fv8gl"), ExtResource("43_qvne6"), ExtResource("44_haub2"), ExtResource("45_xs8xk"), SubResource("Resource_t5ykj"), ExtResource("47_6w4y8"), ExtResource("48_ck35a"), ExtResource("49_cvgxw"), ExtResource("50_6ke0d"), ExtResource("57_e755i")])
missions = Array[ExtResource("26_tel4y")]([ExtResource("27_qkpxi")])
mission_ui = NodePath("MissionPanel")
builder = NodePath("../Builder")
character_scene = ExtResource("18_8lrh8")

@ -0,0 +1,438 @@
[gd_scene load_steps=64 format=3 uid="uid://vgwrcfy1qawf"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Environment" uid="uid://jbptgqvstei3" path="res://scenes/main-environment.tres" id="1_yndf3"]
[ext_resource type="Script" uid="uid://uxn26t1x4ehr" path="res://scripts/structure.gd" id="2_54v6r"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_bwyku"]
[ext_resource type="Texture2D" uid="uid://cbk07cxgshg26" path="res://sprites/selector.png" id="4_wr1wv"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="5_v5o2m"]
<<<<<<< Updated upstream
[ext_resource type="Resource" uid="uid://ccb475jeg7ym5" path="res://structures/grass-trees.tres" id="6_fwsy4"]
[ext_resource type="Script" uid="uid://d3n8yylhejdoh" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Resource" uid="uid://tm532uesguhk" path="res://structures/grass.tres" id="9_2t3p4"]
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="10_ii8xx"]
[ext_resource type="Script" uid="uid://otnxt4e77gmc" path="res://scripts/mission/mission_manager.gd" id="10_oe3re"]
[ext_resource type="Resource" uid="uid://mxrnqinnsqnt" path="res://structures/road-straight-lightposts.tres" id="11_20frt"]
[ext_resource type="Resource" uid="uid://c4qbn3d85prxx" path="res://structures/power-plant.tres" id="12_xtc7p"]
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://dnquivpg0ead" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/unit_1.02/census_planning_1.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/unit_1.02/census_planning_2.tres" id="16_rl54y"]
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://mission/unit_1.02/census_planning_3.tres" id="17_rrdy6"]
=======
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="7_2wyq8"]
[ext_resource type="Script" uid="uid://cu76h00erned0" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Script" uid="uid://bnpqjt5n1s20m" path="res://scripts/mission/mission_manager.gd" id="10_oe3re"]
[ext_resource type="PackedScene" uid="uid://c03crmaif3bss" path="res://scenes/economy_manager.tscn" id="11_2f3dj"]
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Script" uid="uid://bxuxhmlcqp64g" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://d1rme8o0ewiet" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://52tdmmosdbpa" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Script" uid="uid://c6f12a8vs3hp6" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/first_mission.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/second_mission.tres" id="16_rl54y"]
>>>>>>> Stashed changes
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="17_vlub6"]
[ext_resource type="PackedScene" uid="uid://b4gkfwf4i3ydl" path="res://scenes/character.tscn" id="18_8lrh8"]
[ext_resource type="PackedScene" uid="uid://cgk66f6rg03mj" path="res://scenes/hud.tscn" id="18_hud"]
[ext_resource type="PackedScene" uid="uid://bqjnp7uypupog" path="res://scenes/controls_panel.tscn" id="19_controls"]
<<<<<<< Updated upstream
[ext_resource type="Script" uid="uid://b5nubrdeseay4" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://442cwthak2pa" path="res://mission/unit_1.02/market_research_1.tres" id="20_ngu16"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="21_y11qv"]
[ext_resource type="Resource" uid="uid://bom5bu47dy5kp" path="res://mission/unit_1.02/market_research_2.tres" id="24_xud6a"]
[ext_resource type="Resource" uid="uid://csrqvfwp63ygr" path="res://mission/unit_1.02/market_research_3.tres" id="25_6hx7u"]
[ext_resource type="Resource" uid="uid://qwiwim2pg88f" path="res://mission/unit_1.02/market_research_4.tres" id="26_lvk23"]
[ext_resource type="PackedScene" uid="uid://cb2rylpbex3ep" path="res://models/building-arcology.glb" id="27_m8wco"]
[ext_resource type="Resource" uid="uid://cfgw8dblm55c5" path="res://mission/unit_1.03_1.05/grid_growth_1.tres" id="27_s0e58"]
[ext_resource type="Resource" uid="uid://ba3ndftq7dht7" path="res://mission/unit_1.03_1.05/grid_growth_2.tres" id="28_hurxs"]
[ext_resource type="Resource" uid="uid://dgimr2v12rjqu" path="res://mission/unit_1.03_1.05/grid_growth_3.tres" id="29_rhn1n"]
[ext_resource type="Resource" uid="uid://dm2o4dq2oml53" path="res://mission/unit_1.03_1.05/grid_growth_4.tres" id="30_4rwkv"]
[ext_resource type="Resource" uid="uid://btwrfq37q8vey" path="res://mission/unit_1.03_1.05/traffic_flow_1.tres" id="31_j2idb"]
[ext_resource type="Resource" uid="uid://cf7gpb4j7gq1g" path="res://mission/unit_1.03_1.05/traffic_flow_2.tres" id="32_ipu0c"]
[ext_resource type="Resource" uid="uid://ddmxjjyxgxyxo" path="res://mission/unit_1.03_1.05/traffic_flow_3.tres" id="33_c0l5e"]
[ext_resource type="Resource" uid="uid://doxd30r8qbgdq" path="res://mission/unit_1.03_1.05/traffic_flow_4.tres" id="34_21t20"]
[ext_resource type="Resource" uid="uid://duaxn13myfx22" path="res://mission/unit_1.06/sustainable_dev_1.tres" id="35_o0bjh"]
[ext_resource type="Resource" uid="uid://fuxb3pfbbwjm" path="res://mission/unit_1.06/sustainable_dev_2.tres" id="36_2wodh"]
[ext_resource type="Resource" uid="uid://byd5jxiutxpky" path="res://mission/unit_1.06/sustainable_dev_3.tres" id="37_psgx1"]
[ext_resource type="Resource" uid="uid://daug1o7kppqit" path="res://mission/unit_1.06/sustainable_dev_4.tres" id="38_hw762"]
[ext_resource type="Resource" uid="uid://cp7tcpktwlrkt" path="res://mission/unit_1.06/urban_planning_1.tres" id="39_ymw5p"]
[ext_resource type="Resource" uid="uid://c3q1afcvwi4rk" path="res://mission/unit_1.06/urban_planning_2.tres" id="40_uggp1"]
[ext_resource type="Resource" uid="uid://ct1k7n2oopwdu" path="res://mission/unit_1.06/urban_planning_3.tres" id="41_f0dxf"]
[ext_resource type="Resource" uid="uid://d1fykuxfmh2q1" path="res://mission/unit_1.06/urban_planning_4.tres" id="42_fv8gl"]
[ext_resource type="Resource" uid="uid://detwnqsq87r30" path="res://mission/unit_1.07/economic_forecast_1.tres" id="43_qvne6"]
[ext_resource type="Resource" uid="uid://bj7tjuknfaeyg" path="res://mission/unit_1.07/economic_forecast_2.tres" id="44_haub2"]
[ext_resource type="Resource" uid="uid://ctyrlnq5cxuiu" path="res://mission/unit_1.07/economic_forecast_3.tres" id="45_xs8xk"]
[ext_resource type="Resource" uid="uid://ct45gjmw5b7pa" path="res://mission/unit_1.07/economic_forecast_4.tres" id="46_pob6d"]
[ext_resource type="Resource" uid="uid://bwrkqv42wk8f" path="res://mission/unit_1.07/resource_alloc_1.tres" id="47_6w4y8"]
[ext_resource type="Resource" uid="uid://d0nblitd4ixir" path="res://mission/unit_1.07/resource_alloc_2.tres" id="48_ck35a"]
[ext_resource type="Resource" uid="uid://cxh8dgf54oimx" path="res://mission/unit_1.07/resource_alloc_3.tres" id="49_cvgxw"]
[ext_resource type="Resource" uid="uid://cpfr2xnjtpcog" path="res://mission/unit_1.07/resource_alloc_4.tres" id="50_6ke0d"]
=======
[ext_resource type="Script" uid="uid://dlqco4kmwr3er" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://bv4r7ebpjdce4" path="res://mission/sixth_mission.tres" id="20_r0ysx"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="PackedScene" uid="uid://b4k3xfm8pd8qw" path="res://scenes/building_selector.tscn" id="22_building_selector"]
>>>>>>> Stashed changes
[sub_resource type="Resource" id="Resource_1gdbm"]
script = ExtResource("14_76jlq")
panel_type = 0
title = "Welcome to Stem City "
body_text = "Welcome to Stem City!
Your goal is to build a thriving community from the ground up. As you complete missions, you'll unlock new structures to expand and improve your city.
As the new city planner, you need to establish a baseline understanding of your growing community. The mayor has requested a comprehensive census to guide future development.
Ready to start planning your city? Click Close to see the controls and begin your first mission!
"
button_text = "Close"
[sub_resource type="Resource" id="Resource_ja86h"]
script = ExtResource("14_76jlq")
panel_type = 1
title = "Welcome to Stem City"
body_text = "Congratulations, City Planner!
You've completed all the guided missions and unlocked the full potential of Stem City. Your understanding of urban planning and STEM concepts has helped create a thriving community.
But the journey doesn't end here! Continue expanding your city using all the structures you've unlocked. Experiment with different layouts, create new neighborhoods, and see how large you can grow your population.
The future of Stem City is now in your hands - keep building and exploring!
"
button_text = "Close"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mission"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.356863, 0.670588, 0.768627, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
shadow_size = 5
shadow_offset = Vector2(2, 2)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lsmd5"]
bg_color = Color(0.196078, 0.196078, 0.196078, 0.960784)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.294118, 0.478431, 0.729412, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
shadow_size = 5
shadow_offset = Vector2(3, 3)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_description"]
bg_color = Color(0.145098, 0.145098, 0.164706, 0.768627)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.294118, 0.478431, 0.729412, 0.301961)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_graph"]
bg_color = Color(0.117647, 0.137255, 0.160784, 0.768627)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.294118, 0.478431, 0.729412, 0.301961)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[node name="Main" type="Node3D"]
script = ExtResource("20_game_manager")
intro_text_resource = SubResource("Resource_1gdbm")
outro_text_resource = SubResource("Resource_ja86h")
[node name="Builder" type="Node3D" parent="." node_paths=PackedStringArray("selector", "selector_container", "view_camera", "gridmap")]
script = ExtResource("1_jybm7")
<<<<<<< Updated upstream
structures = Array[ExtResource("2_54v6r")]([ExtResource("2_bwyku"), ExtResource("5_v5o2m"), ExtResource("6_fwsy4"), ExtResource("21_y11qv"), ExtResource("9_2t3p4"), ExtResource("10_ii8xx"), ExtResource("11_20frt"), ExtResource("12_xtc7p")])
=======
structures = Array[ExtResource("2_54v6r")]([ExtResource("2_bwyku"), ExtResource("5_v5o2m"), ExtResource("7_2wyq8")])
>>>>>>> Stashed changes
selector = NodePath("Selector")
selector_container = NodePath("Selector/Container")
view_camera = NodePath("../View/Camera")
gridmap = NodePath("../GridMap")
[node name="Selector" type="Node3D" parent="Builder"]
[node name="Sprite" type="Sprite3D" parent="Builder/Selector"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.06, 0)
texture = ExtResource("4_wr1wv")
[node name="Container" type="Node3D" parent="Builder/Selector"]
[node name="View" type="Node3D" parent="."]
transform = Transform3D(0.707107, -0.40558, 0.579228, 0, 0.819152, 0.573576, -0.707107, -0.40558, 0.579228, 0, 0, 0)
script = ExtResource("8_yovpv")
[node name="Camera" type="Camera3D" parent="View"]
transform = Transform3D(1, 0, 0, 0, 1, 7.15256e-07, 0, 0, 1, 0, 27.98, 49.5918)
environment = ExtResource("1_yndf3")
fov = 20.0
[node name="EconomyManager" parent="." instance=ExtResource("11_2f3dj")]
[node name="GridMap" type="GridMap" parent="."]
cell_size = Vector3(1, 1, 1)
cell_center_x = false
cell_center_y = false
cell_center_z = false
[node name="Sun" type="DirectionalLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.642788, 0.766044, 0, -0.766045, 0.642787, 0, 0, 0)
light_energy = 2.0
light_indirect_energy = 3.0
light_volumetric_fog_energy = 3.07
shadow_enabled = true
shadow_opacity = 0.75
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="HUD" parent="CanvasLayer" instance=ExtResource("18_hud")]
[node name="BuildingSelector" parent="CanvasLayer" instance=ExtResource("22_building_selector")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 20.0
offset_top = 100.0
offset_right = 220.0
offset_bottom = 200.0
grow_horizontal = 1
grow_vertical = 1
[node name="ControlsPanel" parent="CanvasLayer" instance=ExtResource("19_controls")]
[node name="SoundPanel" parent="CanvasLayer" instance=ExtResource("21_sound_panel")]
anchors_preset = 8
[node name="GenericTextPanel" parent="CanvasLayer" instance=ExtResource("13_7i6dj")]
resource_data = ExtResource("14_76jlq")
[node name="MissionManager" type="Node" parent="." node_paths=PackedStringArray("mission_ui", "builder")]
script = ExtResource("10_oe3re")
<<<<<<< Updated upstream
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("17_rrdy6"), ExtResource("20_ngu16"), ExtResource("24_xud6a"), ExtResource("25_6hx7u"), ExtResource("26_lvk23"), ExtResource("27_s0e58"), ExtResource("28_hurxs"), ExtResource("29_rhn1n"), ExtResource("30_4rwkv"), ExtResource("31_j2idb"), ExtResource("32_ipu0c"), ExtResource("33_c0l5e"), ExtResource("34_21t20"), ExtResource("35_o0bjh"), ExtResource("36_2wodh"), ExtResource("37_psgx1"), ExtResource("38_hw762"), ExtResource("39_ymw5p"), ExtResource("40_uggp1"), ExtResource("41_f0dxf"), ExtResource("42_fv8gl"), ExtResource("43_qvne6"), ExtResource("44_haub2"), ExtResource("45_xs8xk"), ExtResource("46_pob6d"), ExtResource("47_6w4y8"), ExtResource("48_ck35a"), ExtResource("49_cvgxw"), ExtResource("50_6ke0d")])
=======
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("18_h4fpv"), ExtResource("20_r0ysx")])
>>>>>>> Stashed changes
mission_ui = NodePath("MissionPanel")
builder = NodePath("../Builder")
character_scene = ExtResource("18_8lrh8")
[node name="MissionPanel" type="PanelContainer" parent="MissionManager" node_paths=PackedStringArray("mission_title_label", "mission_description_label", "objectives_container")]
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -450.0
offset_top = 20.0
offset_right = -20.0
grow_horizontal = 0
size_flags_vertical = 0
theme_override_styles/panel = SubResource("StyleBoxFlat_mission")
script = ExtResource("13_xvw5w")
mission_title_label = NodePath("MarginContainer/VBoxContainer/MissionTitleLabel")
mission_description_label = NodePath("MarginContainer/VBoxContainer/MissionDescriptionLabel")
objectives_container = NodePath("MarginContainer/VBoxContainer/ObjectivesContainer")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/MissionPanel"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="MissionManager/MissionPanel/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 15
[node name="MissionTitleLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_fonts/font = ExtResource("17_vlub6")
theme_override_font_sizes/font_size = 24
text = "Mission Title"
horizontal_alignment = 1
autowrap_mode = 2
[node name="HSeparator" type="HSeparator" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="MissionDescriptionLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 1
theme_override_font_sizes/font_size = 16
text = "Mission description goes here..."
autowrap_mode = 2
text_overrun_behavior = 4
[node name="HSeparator2" type="HSeparator" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="ObjectivesLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_colors/font_color = Color(0.678431, 0.847059, 1, 1)
theme_override_font_sizes/font_size = 18
text = "Objectives:"
[node name="ObjectivesContainer" type="VBoxContainer" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 10
[node name="LearningPanel" type="PanelContainer" parent="MissionManager"]
visible = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = 50.0
offset_right = -50.0
offset_bottom = -50.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_lsmd5")
script = ExtResource("14_q2ymb")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/LearningPanel"]
layout_mode = 2
theme_override_constants/margin_left = 30
theme_override_constants/margin_top = 30
theme_override_constants/margin_right = 30
theme_override_constants/margin_bottom = 30
[node name="VBoxContainer" type="VBoxContainer" parent="MissionManager/LearningPanel/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 20
[node name="TitleLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_fonts/font = ExtResource("17_vlub6")
theme_override_font_sizes/font_size = 32
text = "Learning Module"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="DescriptionPanel" type="PanelContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 0.5
theme_override_styles/panel = SubResource("StyleBoxFlat_description")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/DescriptionPanel"]
layout_mode = 2
theme_override_constants/margin_left = 15
theme_override_constants/margin_top = 15
theme_override_constants/margin_right = 15
theme_override_constants/margin_bottom = 15
[node name="DescriptionLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 20
text = "Description goes here..."
autowrap_mode = 2
[node name="GraphTitle" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Graph:"
horizontal_alignment = 1
[node name="GraphContainer" type="Panel" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
custom_minimum_size = Vector2(0, 300)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
size_flags_stretch_ratio = 2.0
theme_override_styles/panel = SubResource("StyleBoxFlat_graph")
[node name="AnswerContainer" type="VBoxContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="QuestionLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "How many workers do we need to build 10 houses in a week?"
horizontal_alignment = 1
[node name="AnswerField" type="LineEdit" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 18
placeholder_text = "Enter answer"
alignment = 1
max_length = 5
[node name="CheckButton" type="Button" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 16
text = "Check Answer"
[node name="FeedbackLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
horizontal_alignment = 1
autowrap_mode = 2
text_overrun_behavior = 3
max_lines_visible = 6
[node name="HBoxContainer" type="HBoxContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="CompleteButton" type="Button" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/HBoxContainer"]
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 18
disabled = true
text = "Complete"
[node name="ObjectiveLabel" type="Label" parent="MissionManager"]
offset_right = 40.0
offset_bottom = 23.0
[node name="building-arcology2" parent="." instance=ExtResource("27_m8wco")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, -16, -14)
[connection signal="all_missions_completed" from="MissionManager" to="." method="_on_mission_manager_all_missions_completed"]
[connection signal="mission_started" from="MissionManager" to="." method="_on_mission_manager_mission_started"]
[connection signal="pressed" from="MissionManager/LearningPanel/MarginContainer/VBoxContainer/HBoxContainer/CompleteButton" to="MissionManager/LearningPanel" method="_on_complete_button_pressed"]

@ -0,0 +1,438 @@
[gd_scene load_steps=64 format=3 uid="uid://vgwrcfy1qawf"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Environment" uid="uid://jbptgqvstei3" path="res://scenes/main-environment.tres" id="1_yndf3"]
[ext_resource type="Script" uid="uid://uxn26t1x4ehr" path="res://scripts/structure.gd" id="2_54v6r"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_bwyku"]
[ext_resource type="Texture2D" uid="uid://cbk07cxgshg26" path="res://sprites/selector.png" id="4_wr1wv"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="5_v5o2m"]
<<<<<<< Updated upstream
[ext_resource type="Resource" uid="uid://ccb475jeg7ym5" path="res://structures/grass-trees.tres" id="6_fwsy4"]
[ext_resource type="Script" uid="uid://d3n8yylhejdoh" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Resource" uid="uid://tm532uesguhk" path="res://structures/grass.tres" id="9_2t3p4"]
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="10_ii8xx"]
[ext_resource type="Script" uid="uid://otnxt4e77gmc" path="res://scripts/mission/mission_manager.gd" id="10_oe3re"]
[ext_resource type="Resource" uid="uid://mxrnqinnsqnt" path="res://structures/road-straight-lightposts.tres" id="11_20frt"]
[ext_resource type="Resource" uid="uid://c4qbn3d85prxx" path="res://structures/power-plant.tres" id="12_xtc7p"]
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://dnquivpg0ead" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/unit_1.02/census_planning_1.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/unit_1.02/census_planning_2.tres" id="16_rl54y"]
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://mission/unit_1.02/census_planning_3.tres" id="17_rrdy6"]
=======
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="7_2wyq8"]
[ext_resource type="Script" uid="uid://cu76h00erned0" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Script" uid="uid://bnpqjt5n1s20m" path="res://scripts/mission/mission_manager.gd" id="10_oe3re"]
[ext_resource type="PackedScene" uid="uid://c03crmaif3bss" path="res://scenes/economy_manager.tscn" id="11_2f3dj"]
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Script" uid="uid://bxuxhmlcqp64g" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://d1rme8o0ewiet" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://52tdmmosdbpa" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Script" uid="uid://c6f12a8vs3hp6" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/first_mission.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/second_mission.tres" id="16_rl54y"]
>>>>>>> Stashed changes
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="17_vlub6"]
[ext_resource type="PackedScene" uid="uid://b4gkfwf4i3ydl" path="res://scenes/character.tscn" id="18_8lrh8"]
[ext_resource type="PackedScene" uid="uid://cgk66f6rg03mj" path="res://scenes/hud.tscn" id="18_hud"]
[ext_resource type="PackedScene" uid="uid://bqjnp7uypupog" path="res://scenes/controls_panel.tscn" id="19_controls"]
<<<<<<< Updated upstream
[ext_resource type="Script" uid="uid://b5nubrdeseay4" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://442cwthak2pa" path="res://mission/unit_1.02/market_research_1.tres" id="20_ngu16"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="21_y11qv"]
[ext_resource type="Resource" uid="uid://bom5bu47dy5kp" path="res://mission/unit_1.02/market_research_2.tres" id="24_xud6a"]
[ext_resource type="Resource" uid="uid://csrqvfwp63ygr" path="res://mission/unit_1.02/market_research_3.tres" id="25_6hx7u"]
[ext_resource type="Resource" uid="uid://qwiwim2pg88f" path="res://mission/unit_1.02/market_research_4.tres" id="26_lvk23"]
[ext_resource type="PackedScene" uid="uid://cb2rylpbex3ep" path="res://models/building-arcology.glb" id="27_m8wco"]
[ext_resource type="Resource" uid="uid://cfgw8dblm55c5" path="res://mission/unit_1.03_1.05/grid_growth_1.tres" id="27_s0e58"]
[ext_resource type="Resource" uid="uid://ba3ndftq7dht7" path="res://mission/unit_1.03_1.05/grid_growth_2.tres" id="28_hurxs"]
[ext_resource type="Resource" uid="uid://dgimr2v12rjqu" path="res://mission/unit_1.03_1.05/grid_growth_3.tres" id="29_rhn1n"]
[ext_resource type="Resource" uid="uid://dm2o4dq2oml53" path="res://mission/unit_1.03_1.05/grid_growth_4.tres" id="30_4rwkv"]
[ext_resource type="Resource" uid="uid://btwrfq37q8vey" path="res://mission/unit_1.03_1.05/traffic_flow_1.tres" id="31_j2idb"]
[ext_resource type="Resource" uid="uid://cf7gpb4j7gq1g" path="res://mission/unit_1.03_1.05/traffic_flow_2.tres" id="32_ipu0c"]
[ext_resource type="Resource" uid="uid://ddmxjjyxgxyxo" path="res://mission/unit_1.03_1.05/traffic_flow_3.tres" id="33_c0l5e"]
[ext_resource type="Resource" uid="uid://doxd30r8qbgdq" path="res://mission/unit_1.03_1.05/traffic_flow_4.tres" id="34_21t20"]
[ext_resource type="Resource" uid="uid://duaxn13myfx22" path="res://mission/unit_1.06/sustainable_dev_1.tres" id="35_o0bjh"]
[ext_resource type="Resource" uid="uid://fuxb3pfbbwjm" path="res://mission/unit_1.06/sustainable_dev_2.tres" id="36_2wodh"]
[ext_resource type="Resource" uid="uid://byd5jxiutxpky" path="res://mission/unit_1.06/sustainable_dev_3.tres" id="37_psgx1"]
[ext_resource type="Resource" uid="uid://daug1o7kppqit" path="res://mission/unit_1.06/sustainable_dev_4.tres" id="38_hw762"]
[ext_resource type="Resource" uid="uid://cp7tcpktwlrkt" path="res://mission/unit_1.06/urban_planning_1.tres" id="39_ymw5p"]
[ext_resource type="Resource" uid="uid://c3q1afcvwi4rk" path="res://mission/unit_1.06/urban_planning_2.tres" id="40_uggp1"]
[ext_resource type="Resource" uid="uid://ct1k7n2oopwdu" path="res://mission/unit_1.06/urban_planning_3.tres" id="41_f0dxf"]
[ext_resource type="Resource" uid="uid://d1fykuxfmh2q1" path="res://mission/unit_1.06/urban_planning_4.tres" id="42_fv8gl"]
[ext_resource type="Resource" uid="uid://detwnqsq87r30" path="res://mission/unit_1.07/economic_forecast_1.tres" id="43_qvne6"]
[ext_resource type="Resource" uid="uid://bj7tjuknfaeyg" path="res://mission/unit_1.07/economic_forecast_2.tres" id="44_haub2"]
[ext_resource type="Resource" uid="uid://ctyrlnq5cxuiu" path="res://mission/unit_1.07/economic_forecast_3.tres" id="45_xs8xk"]
[ext_resource type="Resource" uid="uid://ct45gjmw5b7pa" path="res://mission/unit_1.07/economic_forecast_4.tres" id="46_pob6d"]
[ext_resource type="Resource" uid="uid://bwrkqv42wk8f" path="res://mission/unit_1.07/resource_alloc_1.tres" id="47_6w4y8"]
[ext_resource type="Resource" uid="uid://d0nblitd4ixir" path="res://mission/unit_1.07/resource_alloc_2.tres" id="48_ck35a"]
[ext_resource type="Resource" uid="uid://cxh8dgf54oimx" path="res://mission/unit_1.07/resource_alloc_3.tres" id="49_cvgxw"]
[ext_resource type="Resource" uid="uid://cpfr2xnjtpcog" path="res://mission/unit_1.07/resource_alloc_4.tres" id="50_6ke0d"]
=======
[ext_resource type="Script" uid="uid://dlqco4kmwr3er" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://bv4r7ebpjdce4" path="res://mission/sixth_mission.tres" id="20_r0ysx"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="PackedScene" uid="uid://b4k3xfm8pd8qw" path="res://scenes/building_selector.tscn" id="22_building_selector"]
>>>>>>> Stashed changes
[sub_resource type="Resource" id="Resource_1gdbm"]
script = ExtResource("14_76jlq")
panel_type = 0
title = "Welcome to Stem City "
body_text = "Welcome to Stem City!
Your goal is to build a thriving community from the ground up. As you complete missions, you'll unlock new structures to expand and improve your city.
As the new city planner, you need to establish a baseline understanding of your growing community. The mayor has requested a comprehensive census to guide future development.
Ready to start planning your city? Click Close to see the controls and begin your first mission!
"
button_text = "Close"
[sub_resource type="Resource" id="Resource_ja86h"]
script = ExtResource("14_76jlq")
panel_type = 1
title = "Welcome to Stem City"
body_text = "Congratulations, City Planner!
You've completed all the guided missions and unlocked the full potential of Stem City. Your understanding of urban planning and STEM concepts has helped create a thriving community.
But the journey doesn't end here! Continue expanding your city using all the structures you've unlocked. Experiment with different layouts, create new neighborhoods, and see how large you can grow your population.
The future of Stem City is now in your hands - keep building and exploring!
"
button_text = "Close"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mission"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.356863, 0.670588, 0.768627, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
shadow_size = 5
shadow_offset = Vector2(2, 2)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lsmd5"]
bg_color = Color(0.196078, 0.196078, 0.196078, 0.960784)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.294118, 0.478431, 0.729412, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
shadow_size = 5
shadow_offset = Vector2(3, 3)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_description"]
bg_color = Color(0.145098, 0.145098, 0.164706, 0.768627)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.294118, 0.478431, 0.729412, 0.301961)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_graph"]
bg_color = Color(0.117647, 0.137255, 0.160784, 0.768627)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.294118, 0.478431, 0.729412, 0.301961)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[node name="Main" type="Node3D"]
script = ExtResource("20_game_manager")
intro_text_resource = SubResource("Resource_1gdbm")
outro_text_resource = SubResource("Resource_ja86h")
[node name="Builder" type="Node3D" parent="." node_paths=PackedStringArray("selector", "selector_container", "view_camera", "gridmap")]
script = ExtResource("1_jybm7")
<<<<<<< Updated upstream
structures = Array[ExtResource("2_54v6r")]([ExtResource("2_bwyku"), ExtResource("5_v5o2m"), ExtResource("6_fwsy4"), ExtResource("21_y11qv"), ExtResource("9_2t3p4"), ExtResource("10_ii8xx"), ExtResource("11_20frt"), ExtResource("12_xtc7p")])
=======
structures = Array[ExtResource("2_54v6r")]([ExtResource("2_bwyku"), ExtResource("5_v5o2m"), ExtResource("7_2wyq8")])
>>>>>>> Stashed changes
selector = NodePath("Selector")
selector_container = NodePath("Selector/Container")
view_camera = NodePath("../View/Camera")
gridmap = NodePath("../GridMap")
[node name="Selector" type="Node3D" parent="Builder"]
[node name="Sprite" type="Sprite3D" parent="Builder/Selector"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.06, 0)
texture = ExtResource("4_wr1wv")
[node name="Container" type="Node3D" parent="Builder/Selector"]
[node name="View" type="Node3D" parent="."]
transform = Transform3D(0.707107, -0.40558, 0.579228, 0, 0.819152, 0.573576, -0.707107, -0.40558, 0.579228, 0, 0, 0)
script = ExtResource("8_yovpv")
[node name="Camera" type="Camera3D" parent="View"]
transform = Transform3D(1, 0, 0, 0, 1, 7.15256e-07, 0, 0, 1, 0, 27.98, 49.5918)
environment = ExtResource("1_yndf3")
fov = 20.0
[node name="EconomyManager" parent="." instance=ExtResource("11_2f3dj")]
[node name="GridMap" type="GridMap" parent="."]
cell_size = Vector3(1, 1, 1)
cell_center_x = false
cell_center_y = false
cell_center_z = false
[node name="Sun" type="DirectionalLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.642788, 0.766044, 0, -0.766045, 0.642787, 0, 0, 0)
light_energy = 2.0
light_indirect_energy = 3.0
light_volumetric_fog_energy = 3.07
shadow_enabled = true
shadow_opacity = 0.75
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="HUD" parent="CanvasLayer" instance=ExtResource("18_hud")]
[node name="BuildingSelector" parent="CanvasLayer" instance=ExtResource("22_building_selector")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 20.0
offset_top = 100.0
offset_right = 220.0
offset_bottom = 200.0
grow_horizontal = 1
grow_vertical = 1
[node name="ControlsPanel" parent="CanvasLayer" instance=ExtResource("19_controls")]
[node name="SoundPanel" parent="CanvasLayer" instance=ExtResource("21_sound_panel")]
anchors_preset = 8
[node name="GenericTextPanel" parent="CanvasLayer" instance=ExtResource("13_7i6dj")]
resource_data = ExtResource("14_76jlq")
[node name="MissionManager" type="Node" parent="." node_paths=PackedStringArray("mission_ui", "builder")]
script = ExtResource("10_oe3re")
<<<<<<< Updated upstream
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("17_rrdy6"), ExtResource("20_ngu16"), ExtResource("24_xud6a"), ExtResource("25_6hx7u"), ExtResource("26_lvk23"), ExtResource("27_s0e58"), ExtResource("28_hurxs"), ExtResource("29_rhn1n"), ExtResource("30_4rwkv"), ExtResource("31_j2idb"), ExtResource("32_ipu0c"), ExtResource("33_c0l5e"), ExtResource("34_21t20"), ExtResource("35_o0bjh"), ExtResource("36_2wodh"), ExtResource("37_psgx1"), ExtResource("38_hw762"), ExtResource("39_ymw5p"), ExtResource("40_uggp1"), ExtResource("41_f0dxf"), ExtResource("42_fv8gl"), ExtResource("43_qvne6"), ExtResource("44_haub2"), ExtResource("45_xs8xk"), ExtResource("46_pob6d"), ExtResource("47_6w4y8"), ExtResource("48_ck35a"), ExtResource("49_cvgxw"), ExtResource("50_6ke0d")])
=======
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("18_h4fpv"), ExtResource("20_r0ysx")])
>>>>>>> Stashed changes
mission_ui = NodePath("MissionPanel")
builder = NodePath("../Builder")
character_scene = ExtResource("18_8lrh8")
[node name="MissionPanel" type="PanelContainer" parent="MissionManager" node_paths=PackedStringArray("mission_title_label", "mission_description_label", "objectives_container")]
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -450.0
offset_top = 20.0
offset_right = -20.0
grow_horizontal = 0
size_flags_vertical = 0
theme_override_styles/panel = SubResource("StyleBoxFlat_mission")
script = ExtResource("13_xvw5w")
mission_title_label = NodePath("MarginContainer/VBoxContainer/MissionTitleLabel")
mission_description_label = NodePath("MarginContainer/VBoxContainer/MissionDescriptionLabel")
objectives_container = NodePath("MarginContainer/VBoxContainer/ObjectivesContainer")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/MissionPanel"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="MissionManager/MissionPanel/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 15
[node name="MissionTitleLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_fonts/font = ExtResource("17_vlub6")
theme_override_font_sizes/font_size = 24
text = "Mission Title"
horizontal_alignment = 1
autowrap_mode = 2
[node name="HSeparator" type="HSeparator" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="MissionDescriptionLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 1
theme_override_font_sizes/font_size = 16
text = "Mission description goes here..."
autowrap_mode = 2
text_overrun_behavior = 4
[node name="HSeparator2" type="HSeparator" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="ObjectivesLabel" type="Label" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_colors/font_color = Color(0.678431, 0.847059, 1, 1)
theme_override_font_sizes/font_size = 18
text = "Objectives:"
[node name="ObjectivesContainer" type="VBoxContainer" parent="MissionManager/MissionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 10
[node name="LearningPanel" type="PanelContainer" parent="MissionManager"]
visible = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = 50.0
offset_right = -50.0
offset_bottom = -50.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_lsmd5")
script = ExtResource("14_q2ymb")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/LearningPanel"]
layout_mode = 2
theme_override_constants/margin_left = 30
theme_override_constants/margin_top = 30
theme_override_constants/margin_right = 30
theme_override_constants/margin_bottom = 30
[node name="VBoxContainer" type="VBoxContainer" parent="MissionManager/LearningPanel/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 20
[node name="TitleLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_fonts/font = ExtResource("17_vlub6")
theme_override_font_sizes/font_size = 32
text = "Learning Module"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="DescriptionPanel" type="PanelContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 0.5
theme_override_styles/panel = SubResource("StyleBoxFlat_description")
[node name="MarginContainer" type="MarginContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/DescriptionPanel"]
layout_mode = 2
theme_override_constants/margin_left = 15
theme_override_constants/margin_top = 15
theme_override_constants/margin_right = 15
theme_override_constants/margin_bottom = 15
[node name="DescriptionLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 20
text = "Description goes here..."
autowrap_mode = 2
[node name="GraphTitle" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Graph:"
horizontal_alignment = 1
[node name="GraphContainer" type="Panel" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
custom_minimum_size = Vector2(0, 300)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
size_flags_stretch_ratio = 2.0
theme_override_styles/panel = SubResource("StyleBoxFlat_graph")
[node name="AnswerContainer" type="VBoxContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="QuestionLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "How many workers do we need to build 10 houses in a week?"
horizontal_alignment = 1
[node name="AnswerField" type="LineEdit" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 18
placeholder_text = "Enter answer"
alignment = 1
max_length = 5
[node name="CheckButton" type="Button" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 16
text = "Check Answer"
[node name="FeedbackLabel" type="Label" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/AnswerContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
horizontal_alignment = 1
autowrap_mode = 2
text_overrun_behavior = 3
max_lines_visible = 6
[node name="HBoxContainer" type="HBoxContainer" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="CompleteButton" type="Button" parent="MissionManager/LearningPanel/MarginContainer/VBoxContainer/HBoxContainer"]
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 18
disabled = true
text = "Complete"
[node name="ObjectiveLabel" type="Label" parent="MissionManager"]
offset_right = 40.0
offset_bottom = 23.0
[node name="building-arcology2" parent="." instance=ExtResource("27_m8wco")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, -16, -14)
[connection signal="all_missions_completed" from="MissionManager" to="." method="_on_mission_manager_all_missions_completed"]
[connection signal="mission_started" from="MissionManager" to="." method="_on_mission_manager_mission_started"]
[connection signal="pressed" from="MissionManager/LearningPanel/MarginContainer/VBoxContainer/HBoxContainer/CompleteButton" to="MissionManager/LearningPanel" method="_on_complete_button_pressed"]

@ -0,0 +1,44 @@
[gd_scene load_steps=2 format=3 uid="uid://blfrsgh3cct2l"]
[ext_resource type="Script" uid="uid://cprx4wmn43sgk" path="res://scripts/structure_menu.gd" id="1_structure_menu"]
[node name="StructureMenu" type="Control"]
layout_mode = 3
anchors_preset = 0
offset_right = 300.0
offset_bottom = 600.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_structure_menu")
[node name="MenuPanel" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="ScrollContainer" type="ScrollContainer" parent="MenuPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
horizontal_scroll_mode = 0
[node name="ItemsContainer" type="HBoxContainer" parent="MenuPanel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 10
[node name="ToggleButton" type="Button" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_right = 40.0
offset_bottom = 40.0
grow_horizontal = 0
text = ">"

@ -0,0 +1,56 @@
[gd_scene load_steps=2 format=3 uid="uid://b6x8v0j6y5n3q"]
[ext_resource type="Script" uid="uid://dnquivpg0ead" path="res://scripts/mission/learning_panel.gd" id="1_2k4m3"]
[node name="LearningPanel" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_2k4m3")
[node name="PanelContainer" type="PanelContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 20
[node name="TitleLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Learning Panel"
horizontal_alignment = 1
[node name="ContentLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
text = "Welcome to the learning panel!"
autowrap_mode = 3
[node name="SubmitButtonContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="SubmitButton" type="Button" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/SubmitButtonContainer"]
layout_mode = 2
size_flags_horizontal = 4
text = "Submit"

@ -63,7 +63,6 @@ func _physics_process(delta:float)->void:
stuck_timer += delta
if stuck_timer > stuck_threshold:
# Character is stuck, pick a new random target
print("Character stuck at ", global_position, ", picking new target")
pick_random_target()
stuck_timer = 0.0
else:

@ -26,69 +26,145 @@ var disabled: bool = false # Used to disable building functionality
signal structure_placed(structure_index, position) # For our mission flow
func _ready():
var invalid_placement_material: StandardMaterial3D
# Central structure management
var _structures: Array[Structure] = []
# Getter for structures that ensures deduplication
func get_structures() -> Array[Structure]:
return _structures
# Setter for structures that ensures deduplication
func set_structures(new_structures: Array[Structure]) -> void:
print("\n=== Updating Structures ===")
_structures = new_structures
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = _structures
print("=== Structure Update Complete ===\n")
# Function to deduplicate structures
func _deduplicate_structures() -> void:
# Create a new array to store unique structures
var unique_structures: Array[Structure] = []
var seen_paths = {}
# Initialize all structures, skipping duplicates
for i in range(_structures.size()):
var structure = _structures[i]
if not structure:
push_error("Null structure at index " + str(i))
continue
# Skip if no model
if not structure.model:
push_error("Structure at index " + str(i) + " has no model!")
continue
var path = structure.model.resource_path
# Skip if we've seen this path before
if path in seen_paths:
continue
seen_paths[path] = true
# Initialize unlocked property only if it doesn't exist
if not "unlocked" in structure:
structure.unlocked = false
unique_structures.append(structure)
# Replace the original structures array with our deduplicated one
_structures.clear()
_structures.append_array(unique_structures)
# Print final structure list for verification
print("=== Structure Deduplication Complete ===\n")
func _ready():
map = DataMap.new()
plane = Plane(Vector3.UP, Vector3.ZERO)
hud_manager = get_node_or_null("/root/Main/CanvasLayer/HUD")
# Create new MeshLibrary dynamically, can also be done in the editor
# See: https://docs.godotengine.org/en/stable/tutorials/3d/using_gridmaps.html
# Create invalid placement material
invalid_placement_material = StandardMaterial3D.new()
invalid_placement_material.albedo_color = Color(1, 0, 0, 0.5) # Semi-transparent red
invalid_placement_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
var mesh_library = MeshLibrary.new()
# Setup the navigation region if it doesn't exist
setup_navigation_region()
# Setup construction manager
# Create construction manager
construction_manager = BuildingConstructionManager.new()
construction_manager.name = "BuildingConstructionManager" # Set a proper node name
add_child(construction_manager)
# Connect to the construction completion signal
construction_manager.construction_completed.connect(_on_construction_completed)
# Setup the navigation region if it doesn't exist
setup_navigation_region()
# Give the construction manager references it needs
construction_manager.builder = self
construction_manager.nav_region = nav_region
construction_manager.gridmap = gridmap
# Set gridmap cell size to 3 units
if gridmap:
gridmap.cell_size = Vector3(3, 3, 3)
print("\n=== Initializing Structures ===")
# Load all structures from the structures directory
var dir = DirAccess.open("res://structures")
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if file_name.ends_with(".tres"):
var structure = load("res://structures/" + file_name)
if structure:
structures.append(structure)
file_name = dir.get_next()
else:
push_error("Failed to open structures directory!")
# Sound effects now handled in game_manager.gd
# Set initial structures through our centralized system
set_structures(structures)
for structure in structures:
var id = mesh_library.get_last_unused_item_id()
mesh_library.create_item(id)
mesh_library.set_item_mesh(id, get_mesh(structure.model))
# Apply appropriate scaling for buildings and roads
var transform = Transform3D()
if structure.model.resource_path.contains("power_plant"):
# Scale power plant model to be much smaller (0.5x)
transform = transform.scaled(Vector3(0.5, 0.5, 0.5))
else:
# Scale buildings and roads to be consistent (3x)
transform = transform.scaled(Vector3(3.0, 3.0, 3.0))
mesh_library.set_item_mesh_transform(id, transform)
gridmap.mesh_library = mesh_library
# Connect to mission manager's structures_unlocked signal
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager:
mission_manager.structures_unlocked.connect(_on_structures_unlocked)
# Ensure we start with an unlocked structure
var found_unlocked = false
for i in range(structures.size()):
if "unlocked" in structures[i] and structures[i].unlocked:
index = i
found_unlocked = true
print("Starting with unlocked structure: " + structures[i].model.resource_path)
break
# Initially hide the selector since we don't know which structure to show yet
if selector:
selector.visible = false
if not found_unlocked:
print("WARNING: No unlocked structures found at start!")
print("=== Structure Initialization Complete ===\n")
update_structure()
update_cash()
# Override the setter for structures to ensure deduplication
func _set_structures(new_structures: Array[Structure]) -> void:
structures = new_structures
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
# Function to add a structure to the array
func add_structure(structure: Structure) -> void:
structures.append(structure)
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
# Function to remove a structure from the array
func remove_structure(structure: Structure) -> void:
structures.erase(structure)
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
func _process(delta):
# Skip all building functionality if disabled or game is paused
if disabled or get_tree().paused:
@ -113,17 +189,37 @@ func _process(delta):
view_camera.project_ray_origin(get_viewport().get_mouse_position()),
view_camera.project_ray_normal(get_viewport().get_mouse_position()))
var gridmap_position = Vector3(round(world_position.x), 0, round(world_position.z))
# Snap to 3-unit grid
var gridmap_position = Vector3(
round(world_position.x / 3.0) * 3.0,
0,
round(world_position.z / 3.0) * 3.0
)
selector.position = lerp(selector.position, gridmap_position, delta * 40)
action_build(gridmap_position)
# Check for collisions and update visual feedback
var can_place = check_can_place(gridmap_position)
update_placement_visual(can_place)
action_build(gridmap_position, can_place)
action_demolish(gridmap_position)
# Function to check if the mouse is over any UI elements
func is_mouse_over_ui() -> bool:
# Check if mouse is over the structure menu via HUD
if hud_manager and hud_manager.has_method("is_mouse_over_structure_menu") and hud_manager.is_mouse_over_structure_menu():
return true
# Get mouse position
var mouse_pos = get_viewport().get_mouse_position()
# Check building selector panel
var building_selector = get_node_or_null("/root/Main/CanvasLayer/BuildingSelector")
if building_selector and building_selector.selection_panel and building_selector.selection_panel.visible:
var panel_rect = building_selector.selection_panel.get_global_rect()
if panel_rect.has_point(mouse_pos):
return true
# Let's try an extremely simple approach - just check coordinates
# most HUDs are at top of screen
if mouse_pos.y < 100:
@ -209,12 +305,16 @@ func find_mesh_instance(node):
# Build (place) a structure
func action_build(gridmap_position):
func action_build(gridmap_position, can_place: bool):
if Input.is_action_just_pressed("build"):
# Check if the mouse is over any UI elements before building
if is_mouse_over_ui():
return
# Check if we can place here
if not can_place:
return
# Check if the current structure is unlocked before allowing placement
if "unlocked" in structures[index] and not structures[index].unlocked:
print("Cannot build locked structure: " + structures[index].model.resource_path)
@ -228,11 +328,11 @@ func action_build(gridmap_position):
var is_residential = structures[index].type == Structure.StructureType.RESIDENTIAL_BUILDING
# For power plants, we handle them specially
var is_power_plant = structures[index].model.resource_path.contains("power_plant")
# For grass and trees (terrain), we need special handling
# For grass and trees (terrain), we need special handling
var is_terrain = structures[index].type == Structure.StructureType.TERRAIN
# Check if we're in mission 3 (when we should use construction workers)
var use_worker_construction = structures[index].spawn_builder
var use_worker_construction = true
var mission_manager = get_node_or_null("/root/Main/MissionManager")
# Sound effects are handled via game_manager.gd through the structure_placed signal
@ -248,6 +348,9 @@ func action_build(gridmap_position):
# Create a visible road model as a child of the NavRegion3D
_add_road_to_navregion(gridmap_position, index)
# Also add to gridmap for mission tracking
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
# Rebake the navigation mesh after adding the road
rebake_navigation_mesh()
@ -264,20 +367,10 @@ func action_build(gridmap_position):
# We still set the cell item for collision detection
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
elif is_residential or use_worker_construction:
# For residential buildings in mission 3, use construction workers
# Pass the current selector basis to preserve rotation
else:
# For all other buildings, start construction process with worker
var selector_basis = selector.basis
construction_manager.start_construction(gridmap_position, index, selector_basis)
# Don't place the building immediately - it will be placed when construction completes
# We leave gridmap empty for now
# For mission 3, don't update objectives immediately - wait for construction to finish
# See _update_mission_objective_on_completion in building_construction_manager.gd
else:
# For non-road structures or not in mission 3, add to the gridmap as usual
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
if previous_tile != index:
map.cash -= structures[index].price
@ -437,42 +530,61 @@ func action_rotate():
# Toggle between structures to build
func action_structure_toggle():
# Original keyboard controls
if Input.is_action_just_pressed("structure_next"):
# Find the next unlocked structure
var next_index = index
var tried_indices = []
print("\nE key pressed - attempting to switch to next structure")
# First, collect all unlocked structure indices in order
var unlocked_indices = []
for i in range(_structures.size()):
var structure = _structures[i]
if structure.model:
if structure.unlocked:
unlocked_indices.append(i)
while tried_indices.size() < structures.size():
next_index = wrap(next_index + 1, 0, structures.size())
if tried_indices.has(next_index):
break # We've already tried this index, avoid infinite loop
tried_indices.append(next_index)
if unlocked_indices.is_empty():
print("WARNING: No unlocked structures available!")
return
# Check if this structure is unlocked
if "unlocked" in structures[next_index] and structures[next_index].unlocked:
index = next_index
break
# Find the next unlocked structure
var current_pos = unlocked_indices.find(index)
if current_pos == -1:
# If current index is not in unlocked list, start from beginning
index = unlocked_indices[0]
print("Current structure not unlocked, starting from first unlocked: ", index)
else:
# Move to next structure, wrapping around to start if at end
index = unlocked_indices[(current_pos + 1) % unlocked_indices.size()]
print("Moving to next unlocked structure: ", index)
update_structure()
if Input.is_action_just_pressed("structure_previous"):
# Find the previous unlocked structure
var prev_index = index
var tried_indices = []
print("\nQ key pressed - attempting to switch to previous structure")
# First, collect all unlocked structure indices in order
var unlocked_indices = []
for i in range(_structures.size()):
var structure = _structures[i]
if structure.model:
if structure.unlocked:
unlocked_indices.append(i)
while tried_indices.size() < structures.size():
prev_index = wrap(prev_index - 1, 0, structures.size())
if tried_indices.has(prev_index):
break # We've already tried this index, avoid infinite loop
tried_indices.append(prev_index)
if unlocked_indices.is_empty():
print("WARNING: No unlocked structures available!")
return
# Check if this structure is unlocked
if "unlocked" in structures[prev_index] and structures[prev_index].unlocked:
index = prev_index
break
update_structure()
# Find the previous unlocked structure
var current_pos = unlocked_indices.find(index)
if current_pos == -1:
# If current index is not in unlocked list, start from end
index = unlocked_indices[-1]
print("Current structure not unlocked, starting from last unlocked: ", index)
else:
# Move to previous structure, wrapping around to end if at start
index = unlocked_indices[(current_pos - 1 + unlocked_indices.size()) % unlocked_indices.size()]
print("Moving to previous unlocked structure: ", index)
update_structure()
# Update the structure visual in the 'cursor'
func update_structure():
# Clear previous structure preview in selector
@ -480,14 +592,14 @@ func update_structure():
selector_container.remove_child(n)
# Create new structure preview in selector
var _model = structures[index].model.instantiate()
var _model = _structures[index].model.instantiate()
selector_container.add_child(_model)
# Get reference to the selector sprite
var selector_sprite = selector.get_node("Sprite")
# Apply appropriate scaling based on structure type
if structures[index].model.resource_path.contains("power_plant"):
if _structures[index].model.resource_path.contains("power_plant"):
# Scale power plant model to be much smaller (0.5x)
_model.scale = Vector3(0.5, 0.5, 0.5)
# Center the power plant model within the selector
@ -498,7 +610,7 @@ func update_structure():
_model.position.y += 0.0 # No need for Y adjustment with scaling
# Get the selector scale from the structure resource
var scale_factor = structures[index].selector_scale
var scale_factor = _structures[index].selector_scale
selector_sprite.scale = Vector3(scale_factor, scale_factor, scale_factor)
# Sound effects are now handled in game_manager.gd
@ -829,25 +941,13 @@ func _on_construction_completed(position: Vector3):
break
if structure_index >= 0:
# Add the completed building to the gridmap with the correct rotation and structure index
gridmap.set_cell_item(position, structure_index, rotation_index)
# Check if we need to spawn a character for mission 1
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager:
# We DON'T re-emit the structure_placed signal here, because we already
# emitted it when construction started in action_build()
# This prevents double-counting buildings in the HUD
# Now check if we need to manually handle mission 1 character spawning
if mission_manager.current_mission and mission_manager.current_mission.id == "1" and not mission_manager.character_spawned:
mission_manager.character_spawned = true
mission_manager._spawn_character_on_road(position)
# NOTE: We removed the structure_placed signal emission here to fix the population double-counting
else:
# We don't emit the signal anymore to prevent double-counting
pass
else:
# No residential building structure found
pass
@ -857,11 +957,6 @@ func _on_construction_completed(position: Vector3):
# Make sure the navigation mesh is updated
rebake_navigation_mesh()
# Note that mission objective updates are now handled in the construction manager
# to ensure they only occur after construction is complete
# Saving/load
@ -900,3 +995,220 @@ func action_load():
# Make sure any existing NPCs are children of the navigation region
_move_characters_to_navregion()
# Function to check if a structure can be placed at the given position
func check_can_place(pos: Vector3) -> bool:
# Check for existing structures in the gridmap
for cell in gridmap.get_used_cells():
var distance = Vector2(abs(cell.x - pos.x), abs(cell.z - pos.z))
var min_distance = 3.0 # Minimum distance between centers
# If either structure is a road and they're exactly adjacent, allow it
var existing_item = gridmap.get_cell_item(cell)
if (structures[index].type == Structure.StructureType.ROAD and
structures[existing_item].type == Structure.StructureType.ROAD):
if (distance.x == 1 and distance.y == 0) or (distance.x == 0 and distance.y == 1):
continue
# Check if too close
if distance.x < min_distance and distance.y < min_distance:
return false
# Check for roads in the navigation region
if nav_region:
for child in nav_region.get_children():
if child.name.begins_with("Road_"):
var road_pos = Vector3(
float(child.name.split("_")[1]),
0,
float(child.name.split("_")[2])
)
var distance = Vector2(abs(road_pos.x - pos.x), abs(road_pos.z - pos.z))
# If placing a road and they're exactly adjacent, allow it
if structures[index].type == Structure.StructureType.ROAD:
if (distance.x == 1 and distance.y == 0) or (distance.x == 0 and distance.y == 1):
continue
# Check if too close
if distance.x < 3 and distance.y < 3:
return false
# Check for power plants
for child in get_children():
if child.name.begins_with("PowerPlant_"):
var plant_pos = Vector3(
float(child.name.split("_")[1]),
0,
float(child.name.split("_")[2])
)
var distance = Vector2(abs(plant_pos.x - pos.x), abs(plant_pos.z - pos.z))
if distance.x < 3 and distance.y < 3:
return false
# Check for terrain
for child in get_children():
if child.name.begins_with("Terrain_"):
var terrain_pos = Vector3(
float(child.name.split("_")[1]),
0,
float(child.name.split("_")[2])
)
var distance = Vector2(abs(terrain_pos.x - pos.x), abs(terrain_pos.z - pos.z))
if distance.x < 2 and distance.y < 2:
return false
# Check for construction sites
if construction_manager:
for site_pos in construction_manager.construction_sites:
var distance = Vector2(abs(site_pos.x - pos.x), abs(site_pos.z - pos.z))
# Block the entire 3x3 grid space where construction is happening
if distance.x < 3 and distance.y < 3:
return false
# Check for plots (transparent previews of buildings being constructed)
for child in get_children():
if child.name.begins_with("Plot_"):
var plot_pos = Vector3(
float(child.name.split("_")[1]),
0,
float(child.name.split("_")[2])
)
var distance = Vector2(abs(plot_pos.x - pos.x), abs(plot_pos.z - pos.z))
# Block the entire 3x3 grid space where construction is happening
if distance.x < 3 and distance.y < 3:
return false
return true
# Update the visual feedback for placement
func update_placement_visual(can_place: bool):
if not selector_container or selector_container.get_child_count() == 0:
return
# Get the first child (the model)
var model = selector_container.get_child(0)
# Apply materials recursively to all mesh instances
for mesh_instance in _get_all_mesh_instances(model):
if can_place:
# Restore original materials
if mesh_instance.has_meta("original_materials"):
var original_materials = mesh_instance.get_meta("original_materials")
for i in range(original_materials.size()):
mesh_instance.set_surface_override_material(i, original_materials[i])
else:
# Store original materials if not already stored
if not mesh_instance.has_meta("original_materials"):
var materials = []
for i in range(mesh_instance.get_surface_override_material_count()):
materials.append(mesh_instance.get_surface_override_material(i))
mesh_instance.set_meta("original_materials", materials)
# Apply red transparent material
for i in range(mesh_instance.get_surface_override_material_count()):
mesh_instance.set_surface_override_material(i, invalid_placement_material)
# Helper function to get all MeshInstance3D nodes recursively
func _get_all_mesh_instances(node: Node) -> Array:
var mesh_instances = []
if node is MeshInstance3D:
mesh_instances.append(node)
for child in node.get_children():
mesh_instances.append_array(_get_all_mesh_instances(child))
return mesh_instances
# Place a structure at a specific position and rotation
func place_structure(structure_index: int, position: Vector3, rotation: float = 0.0) -> void:
if structure_index < 0 or structure_index >= structures.size():
push_error("Invalid structure index: " + str(structure_index))
return
# Store current index
var previous_index = index
# Set the structure to place
index = structure_index
# Create a gridmap position from the world position
var gridmap_position = Vector3(
round(position.x / 3.0) * 3.0,
0,
round(position.z / 3.0) * 3.0
)
# Check if we can place here
if not check_can_place(gridmap_position):
push_error("Cannot place structure at position: " + str(position))
index = previous_index
return
# Place the structure
var is_road = structures[index].type == Structure.StructureType.ROAD
var is_residential = structures[index].type == Structure.StructureType.RESIDENTIAL_BUILDING
var is_power_plant = structures[index].model.resource_path.contains("power_plant")
var is_terrain = structures[index].type == Structure.StructureType.TERRAIN
if is_road:
# For roads, we'll need to track in our data without using the GridMap
# But for now, we won't add it to the GridMap visually, just add to NavRegion3D
var previous_tile = gridmap.get_cell_item(gridmap_position)
if previous_tile >= 0 and previous_tile < structures.size() and structures[previous_tile].type == Structure.StructureType.ROAD:
_remove_road_from_navregion(gridmap_position)
_add_road_to_navregion(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_residential:
# For residential buildings, we use the construction manager
construction_manager.start_construction(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_power_plant:
# For power plants, we just place them directly in the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_terrain:
# For grass and trees (terrain), we just place them directly in the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
else:
# For other structures, we use the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
# Restore previous index
index = previous_index
# New function to handle when structures are unlocked
func _on_structures_unlocked():
print("\n=== Structures Unlocked, Setting Initial Structure ===")
# Find the first unlocked structure in the array
var found_unlocked = false
print("\nChecking for unlocked structures:")
for i in range(_structures.size()):
var structure = _structures[i]
print("Structure " + str(i) + ": " + structure.model.resource_path + " - Unlocked: " + str(structure.unlocked))
if structure.unlocked:
index = i
found_unlocked = true
print("Found first unlocked structure at index " + str(i) + ": " + structure.model.resource_path)
break
if not found_unlocked:
print("WARNING: No unlocked structures found!")
# Don't set any structure as selected if none are unlocked
index = -1
# Hide the selector since we have no structures to place
if selector:
selector.visible = false
else:
# Show the selector since we have a structure to place
if selector:
selector.visible = true
update_structure()
print("=== Initial Structure Set ===\n")
# Function to deduplicate structures

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,483 @@
extends PanelContainer
const GenericText = preload("res://resources/generic_text_panel.resource.gd")
signal closed
@export var resource_data: GenericText
@onready var main_button: Button = $MainButton
@onready var selection_panel: Panel = $SelectionPanel
@onready var ground_options = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection/GroundOptions
@onready var building_options = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection/BuildingOptions
@onready var search_bar: LineEdit = $SelectionPanel/MarginContainer/VBoxContainer/SearchBar
@onready var filter_buttons: HBoxContainer = $SelectionPanel/MarginContainer/VBoxContainer/FilterButtons
@onready var description_panel: Panel = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel
@onready var title_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/TitleLabel
@onready var description_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/DescriptionLabel
@onready var price_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PriceLabel
@onready var population_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PopulationLabel
@onready var power_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PowerLabel
@onready var click_blocker: ColorRect = $ClickBlocker
var builder: Node
var current_selection: int = 0
var is_panel_visible: bool = false
var current_filter: String = "All"
var search_text: String = ""
var slide_tween: Tween
var tween: Tween
func _ready() -> void:
# Hide the panel initially
visible = false
# Make sure this control blocks mouse input from passing through
mouse_filter = Control.MOUSE_FILTER_STOP
# Initialize the panel state
if selection_panel:
selection_panel.visible = false
selection_panel.position = Vector2(0, 0) # Reset position
selection_panel.mouse_filter = Control.MOUSE_FILTER_STOP
selection_panel.size = Vector2(300, get_viewport_rect().size.y) # Set a fixed width
# Set up click blocker
if click_blocker:
click_blocker.visible = false
click_blocker.mouse_filter = Control.MOUSE_FILTER_STOP
click_blocker.size = get_viewport_rect().size
click_blocker.position = Vector2.ZERO
click_blocker.z_index = 1000 # Ensure it's above everything else
# Create background panel for the main button
var button_bg = ColorRect.new()
button_bg.color = Color(0, 0, 0, 0.7) # Semi-transparent black
button_bg.size = Vector2(40, 40) # Slightly larger than the button
button_bg.position = Vector2(-5, -5) # Offset to center the button
button_bg.mouse_filter = Control.MOUSE_FILTER_STOP # Block clicks on background
add_child(button_bg)
button_bg.z_index = -1 # Place behind the button
if main_button:
main_button.text = ""
main_button.mouse_filter = Control.MOUSE_FILTER_STOP
# Set initial position to stick to the right side
main_button.position.x = 0
# Center the button vertically
main_button.position.y = get_viewport_rect().size.y / 2 - 20
# Style the button
main_button.add_theme_color_override("font_color", Color(1, 1, 1)) # White text
main_button.add_theme_font_size_override("font_size", 24) # Larger font
# Connect signals
if main_button and not main_button.pressed.is_connected(_on_main_button_pressed):
main_button.pressed.connect(_on_main_button_pressed)
if search_bar and not search_bar.text_changed.is_connected(_on_search_text_changed):
search_bar.text_changed.connect(_on_search_text_changed)
for button in filter_buttons.get_children():
if button is Button and not button.pressed.is_connected(_on_filter_button_pressed.bind(button.text)):
button.pressed.connect(_on_filter_button_pressed.bind(button.text))
# Set mouse filters for containers
if ground_options:
ground_options.mouse_filter = Control.MOUSE_FILTER_STOP
if building_options:
building_options.mouse_filter = Control.MOUSE_FILTER_STOP
# Create initial buttons if builder is set
if builder:
_create_filter_buttons()
_create_option_buttons()
# Apply resource data if available
if resource_data:
apply_resource_data(resource_data)
func show_panel() -> void:
visible = true
is_panel_visible = true
selection_panel.visible = true
click_blocker.visible = true
main_button.text = ""
# Create tween for smooth animation
if tween:
tween.kill()
tween = create_tween()
# Panel is opening
selection_panel.position.x = -selection_panel.size.x
tween.tween_property(selection_panel, "position:x", 0, 0.2)
tween.parallel().tween_property(main_button, "position:x", selection_panel.size.x, 0.2)
# Pause the game when the panel is open
get_tree().paused = true
func hide_panel() -> void:
is_panel_visible = false
# Create tween for smooth animation
if tween:
tween.kill()
tween = create_tween()
# Panel is closing
tween.tween_property(selection_panel, "position:x", -selection_panel.size.x, 0.2)
tween.parallel().tween_property(main_button, "position:x", 0, 0.2)
tween.tween_callback(func():
selection_panel.visible = false
click_blocker.visible = false
main_button.text = ""
visible = false
# Resume the game when the panel is closed
get_tree().paused = false
# Emit signal that panel was closed
closed.emit()
)
func _on_main_button_pressed() -> void:
if !selection_panel or !main_button or !click_blocker:
return
if !is_panel_visible:
show_panel()
else:
hide_panel()
func _create_filter_buttons():
if not filter_buttons or not builder:
return
# Clear existing buttons
for child in filter_buttons.get_children():
child.queue_free()
# Create "All" button
var all_button = Button.new()
all_button.text = "All"
all_button.toggle_mode = true
all_button.button_pressed = true
all_button.flat = true
all_button.pressed.connect(_on_filter_button_pressed.bind("All"))
filter_buttons.add_child(all_button)
# Get unique structure types
var structure_types = {}
for structure in builder.get_structures():
if structure.type == Structure.StructureType.TERRAIN:
structure_types["Ground"] = true
else:
structure_types["Buildings"] = true
# Create buttons for each structure type
for type_name in structure_types.keys():
var button = Button.new()
button.text = type_name
button.toggle_mode = true
button.flat = true
button.pressed.connect(_on_filter_button_pressed.bind(type_name))
filter_buttons.add_child(button)
func _create_option_buttons():
# Clear existing buttons
if ground_options:
for child in ground_options.get_children():
child.queue_free()
if building_options:
for child in building_options.get_children():
child.queue_free()
# Get structures from builder
if not builder:
print("ERROR: No builder reference in building selector")
return
var structures = builder.get_structures()
if not structures or structures.size() == 0:
print("WARNING: No structures available in builder")
return
# Create ground options (grass, pavement, etc.)
var ground_structures = []
var building_structures = []
# Sort structures by type and apply filters
for structure in structures:
if not structure:
continue
# Apply search filter
if search_text != "" and not structure.title.to_lower().contains(search_text.to_lower()):
continue
# Apply type filter
if current_filter != "All":
match current_filter:
"Ground":
if structure.type != Structure.StructureType.TERRAIN:
continue
"Buildings":
if structure.type == Structure.StructureType.TERRAIN:
continue
# Add to appropriate list
if structure.type == Structure.StructureType.TERRAIN:
ground_structures.append(structure)
else:
building_structures.append(structure)
# Set up grid layout for ground options
if ground_options:
ground_options.columns = 4
ground_options.add_theme_constant_override("h_separation", 10)
ground_options.add_theme_constant_override("v_separation", 10)
# Create buttons for ground structures
for i in range(ground_structures.size()):
var button = _create_option_button(ground_structures[i], i)
if ground_options:
ground_options.add_child(button)
# Set up grid layout for building options
if building_options:
building_options.columns = 4
building_options.add_theme_constant_override("h_separation", 10)
building_options.add_theme_constant_override("v_separation", 10)
# Create buttons for building structures
for i in range(building_structures.size()):
var button = _create_option_button(building_structures[i], i + ground_structures.size())
if building_options:
building_options.add_child(button)
# Hide sections if they have no options
var ground_section = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection
var building_section = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection
if ground_section:
ground_section.visible = ground_structures.size() > 0
if building_section:
building_section.visible = building_structures.size() > 0
# Force update the layout
if ground_options:
ground_options.queue_redraw()
if building_options:
building_options.queue_redraw()
func _create_option_button(structure: Structure, index: int) -> Button:
var button = Button.new()
button.custom_minimum_size = Vector2(70, 70) # Reduced from 80x80
button.toggle_mode = true
button.button_group = ButtonGroup.new()
button.tooltip_text = structure.description
# Create container for button contents
var container = VBoxContainer.new()
container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_CENTER
container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
container.add_theme_constant_override("separation", 2) # Reduce spacing between elements
button.add_child(container)
# Create preview container
var preview_container = Control.new()
preview_container.custom_minimum_size = Vector2(50, 50) # Reduced from 64x64
container.add_child(preview_container)
# Add preview image or model
if structure.thumbnail and structure.thumbnail != "Thumbnail" and ResourceLoader.exists(structure.thumbnail):
var preview = TextureRect.new()
var texture = load(structure.thumbnail)
preview.texture = texture
preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL
preview.size_flags_vertical = Control.SIZE_EXPAND_FILL
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
preview.custom_minimum_size = Vector2(50, 50) # Force minimum size
preview.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL # Maintain aspect ratio while fitting width
preview_container.add_child(preview)
else:
var viewport = SubViewport.new()
viewport.size = Vector2i(50, 50) # Reduced from 64x64
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
viewport.transparent_bg = true
preview_container.add_child(viewport)
var camera = Camera3D.new()
camera.position = Vector3(0, 0, 2)
camera.look_at(Vector3.ZERO)
viewport.add_child(camera)
if structure.model:
var model = structure.model.instantiate()
viewport.add_child(model)
# Add structure name
var name_label = Label.new()
name_label.text = structure.title
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
name_label.autowrap_mode = TextServer.AUTOWRAP_WORD
name_label.max_lines_visible = 2
name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
name_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
container.add_child(name_label)
# Add price label
var price_label = Label.new()
price_label.text = "$" + str(structure.price)
price_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
price_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
container.add_child(price_label)
# Add lock overlay if structure is locked
if not structure.unlocked:
var lock_overlay = ColorRect.new()
lock_overlay.color = Color(0, 0, 0, 0.5)
lock_overlay.size = preview_container.size
preview_container.add_child(lock_overlay)
var lock_icon = TextureRect.new()
lock_icon.texture = load("res://textures/lock.png")
lock_icon.size_flags_horizontal = Control.SIZE_EXPAND_FILL
lock_icon.size_flags_vertical = Control.SIZE_EXPAND_FILL
lock_icon.stretch_mode = TextureRect.StretchMode.STRETCH_KEEP_ASPECT_CENTERED
lock_icon.size = Vector2(30, 30) # Reduced from 40x40
lock_icon.position = Vector2(10, 10) # Adjusted position
preview_container.add_child(lock_icon)
# Disable button if structure is locked
button.disabled = true
button.pressed.connect(_on_option_selected.bind(index))
return button
func _on_search_text_changed(new_text: String) -> void:
search_text = new_text
_create_option_buttons()
func _on_filter_button_pressed(filter: String) -> void:
current_filter = filter
_create_option_buttons()
func _on_option_selected(index: int):
if not builder:
print("ERROR: No builder reference in building selector")
return
var structures = builder.get_structures()
if not structures or index < 0 or index >= structures.size():
print("ERROR: Invalid structure index: ", index)
return
current_selection = index
_update_button_states()
# Update the builder's current selection
builder.index = index
builder.update_structure()
# Update the main button text
main_button.text = "Selected: " + _get_structure_name(index)
# Update description panel
_update_description_panel(index)
func _update_button_states():
# Update all buttons to show which one is selected
if not ground_options or not building_options:
return
var all_buttons = []
# Add ground options buttons if they exist
for child in ground_options.get_children():
if child is Button:
all_buttons.append(child)
# Add building options buttons if they exist
for child in building_options.get_children():
if child is Button:
all_buttons.append(child)
# Update button states
for i in range(all_buttons.size()):
if all_buttons[i] is Button:
all_buttons[i].button_pressed = (i == current_selection)
func _get_structure_name(index: int) -> String:
var structures = builder.get_structures()
if structures and index >= 0 and index < structures.size():
var structure = structures[index]
return structure.title
return "Unknown"
func _update_description_panel(index: int):
var structures = builder.get_structures()
if not structures or index < 0 or index >= structures.size():
title_label.text = "No Building Selected"
description_label.text = "Select a building to view its details"
price_label.text = "Price: $0"
population_label.text = "Population: 0"
power_label.text = "Power: 0 kW"
return
var structure = structures[index]
title_label.text = structure.title
description_label.text = structure.description
price_label.text = "Price: $" + str(structure.price)
population_label.text = "Population: " + str(structure.population_count)
var power_text = "Power: "
if structure.kW_production > 0:
power_text += "+" + str(structure.kW_production) + " kW"
elif structure.kW_usage > 0:
power_text += "-" + str(structure.kW_usage) + " kW"
else:
power_text += "0 kW"
power_label.text = power_text
func apply_resource_data(data: GenericText) -> void:
if data:
if title_label:
title_label.text = data.title
if description_label:
description_label.text = data.body_text
# Override _gui_input to ensure we're handling all input
func _gui_input(event: InputEvent) -> void:
if is_panel_visible:
# When panel is visible, accept all input to prevent it from reaching the game
get_viewport().set_input_as_handled()
# Override _input to catch all input events
func _input(event: InputEvent) -> void:
if is_panel_visible and (event is InputEventMouseButton or event is InputEventMouseMotion):
# When panel is visible, accept all mouse input to prevent it from reaching the game
get_viewport().set_input_as_handled()
# If it's a mouse button press, mark it as handled
if event is InputEventMouseButton:
event.pressed = false
# Override _unhandled_input to catch any remaining input events
func _unhandled_input(event: InputEvent) -> void:
if is_panel_visible:
get_viewport().set_input_as_handled()
if event is InputEventMouseButton:
event.pressed = false
# Override _get_global_rect to ensure the builder's UI check detects us
func _get_global_rect() -> Rect2:
if is_panel_visible:
return Rect2(Vector2.ZERO, get_viewport_rect().size)
return Rect2(Vector2.ZERO, Vector2.ZERO)
# Add a method to check if the mouse is over our panel
func is_mouse_over_building_selector() -> bool:
if is_panel_visible:
var mouse_pos = get_viewport().get_mouse_position()
var rect = get_global_rect()
return rect.has_point(mouse_pos)
return false

@ -0,0 +1 @@
uid://bsjmj0qu3xfrr

@ -0,0 +1,93 @@
extends Node
# Resource parameters
var money: float = 1000.0
var population: int = 0
var energy_consumption: float = 0.0
var energy_production: float = 0.0
# Economic parameters
var money_per_population: float = 1.0 # Money earned per population per second
var base_energy_cost: float = 0.1 # Base energy cost per population
var tax_rate: float = 0.1 # Tax rate on population income
# Building costs
var building_costs := {
"small_house": 100,
"medium_house": 250,
"large_house": 500,
"power_plant": 1000,
"road": 50
}
# Building effects
var building_population := {
"small_house": 10,
"medium_house": 25,
"large_house": 50
}
var building_energy := {
"power_plant": 100.0, # Energy produced per second
"small_house": 5.0, # Energy consumed per second
"medium_house": 12.0,
"large_house": 25.0
}
# Signals
signal money_changed(new_amount: float)
signal population_changed(new_population: int)
signal energy_balance_changed(production: float, consumption: float)
func _ready():
# Start the economy tick
$EconomyTimer.start()
func _process(_delta):
# Update energy balance
energy_consumption = calculate_total_energy_consumption()
emit_signal("energy_balance_changed", energy_production, energy_consumption)
func calculate_total_energy_consumption() -> float:
var total := 0.0
for building_type in building_energy:
if building_type != "power_plant": # Skip power plants as they produce energy
total += building_energy[building_type] * get_building_count(building_type)
return total
func get_building_count(building_type: String) -> int:
var builder = get_node_or_null("/root/Main/Builder")
if not builder:
return 0
var count = 0
for structure in builder.structures:
if structure.model and structure.model.resource_path.contains(building_type):
count += 1
return count
func can_afford_building(building_type: String) -> bool:
return money >= building_costs.get(building_type, 0)
func purchase_building(building_type: String) -> bool:
if can_afford_building(building_type):
money -= building_costs[building_type]
emit_signal("money_changed", money)
# Update population if it's a house
if building_type in building_population:
population += building_population[building_type]
emit_signal("population_changed", population)
# Update energy if it's a power plant
if building_type == "power_plant":
energy_production += building_energy["power_plant"]
return true
return false
func _on_economy_timer_timeout():
# Calculate income from population
var income = population * money_per_population * tax_rate
money += income
emit_signal("money_changed", money)

@ -0,0 +1 @@
uid://bat3h7fhpdh7

@ -3,9 +3,8 @@ extends Node
# This script handles overall game management tasks, including audio management and UI interactions.
var config = ConfigFile.new()
# Sound manager reference
var sound_manager: Node
var music_player: AudioStreamPlayer
var building_sfx: AudioStreamPlayer
@ -17,50 +16,81 @@ var construction_sfx: AudioStreamPlayer
@export var intro_text_resource: GenericText
@export var outro_text_resource: GenericText
# Node references
@onready var building_selector = get_node_or_null("CanvasLayer/BuildingSelector")
@onready var resource_display = get_node_or_null("CanvasLayer/ResourceDisplay")
@onready var game_menu = get_node_or_null("CanvasLayer/GameMenu")
func _ready():
print("GameManager: Initializing...")
# Load data from a file.
#var err = config.load("global://config.cfg")
## If the file didn't load, ignore it.
#if err != OK:
#return
var err = config.load("user://config.cfg")
# If the file didn't load, ignore it.
if err != OK:
print("GameManager: No config file found, using defaults")
config = ConfigFile.new()
# Get sound manager reference
sound_manager = get_node_or_null("/root/SoundManager")
if sound_manager:
print("GameManager: Found sound manager, connecting signals")
# Connect to sound manager signals
sound_manager.music_volume_changed.connect(_on_music_volume_changed)
sound_manager.sfx_volume_changed.connect(_on_sfx_volume_changed)
sound_manager.music_muted_changed.connect(_on_music_muted_changed)
sound_manager.sfx_muted_changed.connect(_on_sfx_muted_changed)
# Load saved volume settings
var saved_music_volume = config.get_value("audio", "music_volume", 0.1)
var saved_sfx_volume = config.get_value("audio", "sfx_volume", 0.1)
var saved_music_muted = config.get_value("audio", "music_muted", false)
var saved_sfx_muted = config.get_value("audio", "sfx_muted", false)
print("GameManager: Loading saved settings - Music: ", saved_music_volume, " SFX: ", saved_sfx_volume)
print("GameManager: Loading saved mute states - Music: ", saved_music_muted, " SFX: ", saved_sfx_muted)
# Apply saved settings
sound_manager.music_volume = saved_music_volume
sound_manager.sfx_volume = saved_sfx_volume
sound_manager.music_muted = saved_music_muted
sound_manager.sfx_muted = saved_sfx_muted
else:
print("GameManager: Warning - Sound manager not found!")
# Register SoundManager in the main loop for JavaScript bridge to find
Engine.get_main_loop().set_meta("sound_manager", get_node_or_null("/root/SoundManager"))
Engine.get_main_loop().set_meta("sound_manager", sound_manager)
# Reference to the controls panel, sound panel, and HUD
var controls_panel = $CanvasLayer/ControlsPanel
var sound_panel = $CanvasLayer/SoundPanel
var hud = $CanvasLayer/HUD
# Reference to the controls panel and HUD
var controls_panel = get_node_or_null("CanvasLayer/ControlsPanel")
var hud = get_node_or_null("CanvasLayer/HUD")
# Set up the HUD's reference to the panels
hud.controls_panel = controls_panel
hud.sound_panel = sound_panel
if hud and controls_panel:
hud.controls_panel = controls_panel
# Show intro text if available
#if generic_text_panel and intro_text_resource:
#generic_text_panel.apply_resource_data(intro_text_resource)
#generic_text_panel.show_panel()
#
#generic_text_panel.closed.connect(func():
#if generic_text_panel and generic_text_panel.resource_data and generic_text_panel.resource_data.panel_type == 0 and controls_panel:
#controls_panel.show_panel()
#)
if generic_text_panel and intro_text_resource:
generic_text_panel.apply_resource_data(intro_text_resource)
generic_text_panel.show_panel()
generic_text_panel.closed.connect(func():
if generic_text_panel.resource_data.panel_type == 0 and controls_panel:
controls_panel.show_panel()
)
# Auto-show controls at start
# Connect controls panel closed signal
if controls_panel:
controls_panel.closed.connect(_on_controls_panel_closed)
# Check for audio initialization status (important for web)
var sound_manager = get_node_or_null("/root/SoundManager")
var can_initialize_audio = true
#
# if OS.has_feature("web") and sound_manager:
# can_initialize_audio = sound_manager.audio_initialized
#
# if not can_initialize_audio:
# # For web, wait for the audio_ready signal before initializing audio
# sound_manager.audio_ready.connect(_initialize_game_audio)
if OS.has_feature("web") and sound_manager:
can_initialize_audio = sound_manager.audio_initialized
if not can_initialize_audio:
# For web, wait for the audio_ready signal before initializing audio
sound_manager.audio_ready.connect(_initialize_game_audio)
# Set up audio if allowed (immediate for desktop, after interaction for web)
if can_initialize_audio:
@ -70,14 +100,81 @@ func _ready():
var builder = get_node_or_null("/root/Main/Builder")
if builder:
builder.structure_placed.connect(_on_structure_placed)
print("GameManager: Connected to Builder signals")
# Connect to construction signals via deferred call to make sure everything is ready
call_deferred("_setup_construction_signals")
# Make sure sound buses are properly configured
call_deferred("_setup_sound_buses")
# Connect the building selector to the builder
if building_selector:
if builder:
building_selector.builder = builder
print("GameManager: Connected BuildingSelector to Builder")
else:
print("GameManager: Warning - Builder not found!")
else:
print("GameManager: Warning - BuildingSelector not found!")
# Connect builder's cash display to HUD
if builder and hud:
builder.cash_display = hud.get_node("HBoxContainer/CashItem/CashLabel")
# Start background music
_start_background_music()
# Connect economy manager signals to HUD
var economy_manager = get_node_or_null("EconomyManager")
var hud_manager = get_node_or_null("CanvasLayer/HUD")
if economy_manager and hud_manager:
economy_manager.money_changed.connect(hud_manager.update_money)
economy_manager.population_changed.connect(hud_manager.update_population_count)
economy_manager.energy_balance_changed.connect(hud_manager.update_energy_balance)
# Initialize all game audio - called immediately on desktop, after user interaction on web
# Initialize managers
_initialize_managers()
# Connect signals
_connect_signals()
# Initialize game state
_initialize_game_state()
# Start the game
start_game()
func _on_music_volume_changed(new_volume: float):
print("GameManager: Music volume changed to ", new_volume)
config.set_value("audio", "music_volume", new_volume)
var err = config.save("user://config.cfg")
if err != OK:
print("GameManager: Error saving music volume: ", err)
func _on_sfx_volume_changed(new_volume: float):
print("GameManager: SFX volume changed to ", new_volume)
config.set_value("audio", "sfx_volume", new_volume)
var err = config.save("user://config.cfg")
if err != OK:
print("GameManager: Error saving SFX volume: ", err)
func _on_music_muted_changed(is_muted: bool):
print("GameManager: Music mute changed to ", is_muted)
config.set_value("audio", "music_muted", is_muted)
var err = config.save("user://config.cfg")
if err != OK:
print("GameManager: Error saving music mute: ", err)
func _on_sfx_muted_changed(is_muted: bool):
print("GameManager: SFX mute changed to ", is_muted)
config.set_value("audio", "sfx_muted", is_muted)
var err = config.save("user://config.cfg")
if err != OK:
print("GameManager: Error saving SFX mute: ", err)
# Initialize all game audio - called immediately on desktop, after user interaction for web
func _initialize_game_audio():
# Set up all audio systems
setup_background_music()
@ -87,17 +184,6 @@ func _initialize_game_audio():
# This function is called when the controls panel is closed
func _on_controls_panel_closed():
pass
# This is the perfect place to initialize audio for web builds
# since we know the user has interacted with the game
# if OS.has_feature("web"):
# # Force initialize the sound manager (will have no effect if already initialized)
# var sound_manager = get_node_or_null("/root/SoundManager")
# if sound_manager and not sound_manager.audio_initialized:
# sound_manager._initialize_web_audio()
#
# # Make sure our music is playing
# if music_player and music_player.stream and not music_player.playing:
# music_player.play()
# Function to set up the sound buses
func _setup_sound_buses():
@ -149,6 +235,10 @@ func setup_background_music():
music_player.stream = music
music_player.bus = "Music" # Use the Music bus
# Set initial volume
if sound_manager:
music_player.volume_db = sound_manager.linear_to_db(sound_manager.music_volume)
# Check if we can play audio immediately (desktop) or need to wait (web)
var can_play_now = true
if OS.has_feature("web"):
@ -164,12 +254,17 @@ func setup_background_music():
if can_play_now:
_start_background_music()
else:
print("GameManager: Warning - Could not load music file!")
# Try a fallback sound as music
var fallback_sound = load("res://sounds/building_placing.wav")
if fallback_sound:
music_player.stream = fallback_sound
music_player.bus = "Music"
# Set initial volume
if sound_manager:
music_player.volume_db = sound_manager.linear_to_db(sound_manager.music_volume)
# Check if we can play immediately
var can_play_now = true
if OS.has_feature("web"):
@ -179,6 +274,8 @@ func setup_background_music():
if can_play_now:
music_player.play()
else:
print("GameManager: Error - Could not load fallback sound!")
# Start background music playing (called directly or via signal)
func _start_background_music():
@ -280,8 +377,6 @@ func stop_construction_sound():
# but we need to keep this function for backward compatibility
pass
# Removed duplicate _retry_music_play function that was here
# Setup construction signals properly
func _setup_construction_signals():
var builder = get_node_or_null("/root/Main/Builder")
@ -324,3 +419,23 @@ func _on_mission_manager_mission_started(mission: MissionData) -> void:
generic_text_panel.apply_resource_data(mission_text)
generic_text_panel.show_panel()
func _initialize_managers():
print("GameManager: Initializing managers")
# Initialize any required managers here
pass
func _connect_signals():
print("GameManager: Connecting signals")
# Connect any required signals here
pass
func _initialize_game_state():
print("GameManager: Initializing game state")
# Initialize game state here
pass
func start_game():
print("GameManager: Starting game")
# Start game logic here
pass

@ -24,14 +24,14 @@ var population_tooltip: Control
var electricity_tooltip: Control
var controls_panel: PanelContainer
var sound_panel: PanelContainer
var builder: Node
var structure_menu: Control
@onready var _builder = get_node_or_null("/root/Main/Builder")
func _ready():
# Connect to signals from the builder
builder = get_node_or_null("/root/Main/Builder")
if builder:
builder.structure_placed.connect(_on_structure_placed)
builder.structure_removed.connect(_on_structure_removed)
if _builder:
_builder.structure_placed.connect(_on_structure_placed)
_builder.structure_removed.connect(_on_structure_removed)
# Initialize UI elements
population_label = $HBoxContainer/PopulationItem/PopulationLabel
@ -44,14 +44,24 @@ func _ready():
# Get references to panels
controls_panel = get_node_or_null("/root/Main/CanvasLayer/ControlsPanel")
sound_panel = get_node_or_null("/root/Main/CanvasLayer/SoundPanel")
structure_menu = get_node_or_null("/root/Main/CanvasLayer/StructureMenu")
# Setup mission select button
if mission_select_button:
mission_select_button.connect("pressed", _on_mission_select_button_pressed)
if not mission_select_button.pressed.is_connected(_on_mission_select_button_pressed):
mission_select_button.pressed.connect(_on_mission_select_button_pressed)
else:
push_error("Mission select button not found in HUD")
# Setup mission select menu
_setup_mission_select_menu()
# Setup structure menu
_setup_structure_menu()
# Wait a frame to ensure all nodes are ready
await get_tree().process_frame
# Update mission select visibility based on export variable
_update_mission_select_visibility()
@ -93,25 +103,54 @@ func _setup_mission_select_menu():
var canvas_layer = get_node_or_null("/root/Main/CanvasLayer")
if canvas_layer:
canvas_layer.add_child(mission_select_menu)
#mission_select_menu.hide() # Initially hidden
# Make sure it's initially hidden
mission_select_menu.hide()
# Set up the structure menu
func _setup_structure_menu():
# Check if the structure menu already exists
structure_menu = get_node_or_null("/root/Main/CanvasLayer/StructureMenu")
# If not, instantiate and add it
if not structure_menu:
var structure_menu_scene = load("res://scenes/structure_menu.tscn")
if structure_menu_scene:
structure_menu = structure_menu_scene.instantiate()
var canvas_layer = get_node_or_null("/root/Main/CanvasLayer")
if canvas_layer:
# Use call_deferred to add the child
canvas_layer.add_child.call_deferred(structure_menu)
# Set the builder reference after a frame to ensure the node is added
await get_tree().process_frame
if _builder:
structure_menu.builder = _builder
# Update mission select visibility based on export variable
func _update_mission_select_visibility():
var mission_select_item = $HBoxContainer/MissionSelectItem
if not is_inside_tree():
# If we're not in the tree yet, wait until we are
await ready
var mission_select_item = get_node_or_null("HBoxContainer/MissionSelectItem")
if mission_select_item:
mission_select_item.visible = show_mission_select
else:
push_warning("MissionSelectItem node not found in HUD")
# Handle mission select button press
func _on_mission_select_button_pressed():
print("Mission select button pressed")
# Make sure the menu exists
if not mission_select_menu:
_setup_mission_select_menu()
if mission_select_menu:
print("Toggling mission select menu visibility")
mission_select_menu.toggle_visibility()
else:
# Try to set up the menu if it doesn't exist yet
_setup_mission_select_menu()
if mission_select_menu:
mission_select_menu.show()
push_error("Mission select menu not found after setup attempt")
func _process(delta):
# Update the population label if it changes
if population_label and Globals.population != total_population:
@ -121,10 +160,10 @@ func _process(delta):
# Called when a structure is placed
func _on_structure_placed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
if !_builder or structure_index < 0 or structure_index >= _builder.structures.size():
return
var structure = builder.structures[structure_index]
var structure = _builder.structures[structure_index]
# Only update population for non-residential buildings or if we're NOT in the construction mission
var is_residential = structure.type == Structure.StructureType.RESIDENTIAL_BUILDING
@ -146,13 +185,13 @@ func _on_structure_placed(structure_index, position):
# Called when a structure is removed
func _on_structure_removed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
if !_builder or structure_index < 0 or structure_index >= _builder.structures.size():
return
var structure = builder.structures[structure_index]
var structure = _builder.structures[structure_index]
# Update population (but only for non-residential buildings in mission 3)
# For residential buildings in mission 3, we handle population separately in builder._remove_resident_for_building
# For residential buildings in mission 3, we handle population separately in _builder._remove_resident_for_building
var skip_population_update = false
var mission_manager = get_node_or_null("/root/Main/MissionManager")
@ -161,7 +200,7 @@ func _on_structure_removed(structure_index, position):
# Only update population for one resident, since we're removing them one by one
# We don't do total reset based on structure.population_count
skip_population_update = true
# We decrement by 1 in builder._remove_resident_for_building instead
# We decrement by 1 in _builder._remove_resident_for_building instead
if !skip_population_update:
total_population = max(0, total_population - structure.population_count)
@ -257,3 +296,24 @@ func _on_help_button_pressed():
if controls_panel:
controls_panel.show_panel()
# Called when the music volume is changed
func _on_music_volume_changed(new_volume):
pass # Sound panel handles this through signals
# Called when the sfx volume is changed
func _on_sfx_volume_changed(new_volume):
pass # Sound panel handles this through signals
# Called when the music is muted
func _on_music_muted_changed(is_muted):
pass # Sound panel handles this through signals
# Called when the sfx is muted
func _on_sfx_muted_changed(is_muted):
pass # Sound panel handles this through signals
func is_mouse_over_structure_menu() -> bool:
if structure_menu and structure_menu.has_method("is_mouse_over_menu"):
return structure_menu.is_mouse_over_menu()
return false

@ -0,0 +1,244 @@
extends Node
# Signals
signal electricity_updated(usage, production)
# Variables
var total_population: int = 0
var total_kW_usage: float = 0.0
var total_kW_production: float = 0.0
var current_money: float = 0.0
# References
var buildeJuj
var building_construction_manager
var population_label: Label
var electricity_label: Label
var electricity_indicator: ColorRect
var population_tooltip: Control
var electricity_tooltip: Control
var controls_panel: PanelContainer
var sound_panel: PanelContainer
<<<<<<< Updated upstream
var builder:Node
=======
var cash_label: Label
>>>>>>> Stashed changes
func _ready():
# Connect to signals from the builder
builder = get_node_or_null("/root/Main/Builder")
if builder:
builder.structure_placed.connect(_on_structure_placed)
builder.structure_removed.connect(_on_structure_removed)
# EventBus.population_update.connect(set_population_count)
# Initialize UI elements
population_label = $HBoxContainer/PopulationItem/PopulationLabel
if not population_label:
push_error("Population label not found in HUD!")
return
electricity_label = $HBoxContainer/ElectricityItem/ElectricityContainer/ElectricityValues/ElectricityLabel
if not electricity_label:
push_error("Electricity label not found in HUD!")
return
electricity_indicator = $HBoxContainer/ElectricityItem/ElectricityContainer/ElectricityValues/ElectricityIndicator
if not electricity_indicator:
push_error("Electricity indicator not found in HUD!")
return
population_tooltip = $PopulationTooltip
if not population_tooltip:
push_error("Population tooltip not found in HUD!")
return
electricity_tooltip = $ElectricityTooltip
<<<<<<< Updated upstream
=======
if not electricity_tooltip:
push_error("Electricity tooltip not found in HUD!")
return
cash_label = $HBoxContainer/CashItem/CashLabel
if not cash_label:
push_error("Cash label not found in HUD!")
return
>>>>>>> Stashed changes
# Ensure electricity indicator starts with red color
electricity_indicator.color = Color(1, 0, 0) # Start with red
# Hide the electricity label for now (keeping implementation for later)
electricity_label.visible = false
# Set tooltips
population_tooltip.visible = false
electricity_tooltip.visible = false
# Update HUD
update_hud()
func _process(delta):
# Update the population label if it changes
if population_label and Globals.population != total_population:
total_population = Globals.population
population_label.text = str(total_population)
# Called when a structure is placed
func _on_structure_placed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
return
var structure = builder.structures[structure_index]
# Only update population for non-residential buildings or if we're NOT in the construction mission
var is_residential = structure.type == Structure.StructureType.RESIDENTIAL_BUILDING
var mission_manager = get_node_or_null("/root/Main/MissionManager")
var using_construction = false
if mission_manager and mission_manager.current_mission:
var mission_id = mission_manager.current_mission.id
using_construction = (mission_id == "3" or mission_id == "1")
# Always update electricity usage/production
total_kW_usage += structure.kW_usage
total_kW_production += structure.kW_production
# Update HUD
update_hud()
# Emit signals
electricity_updated.emit(total_kW_usage, total_kW_production)
# Called when a structure is removed
func _on_structure_removed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
return
var structure = builder.structures[structure_index]
# Update population (but only for non-residential buildings in mission 3)
# For residential buildings in mission 3, we handle population separately in builder._remove_resident_for_building
var skip_population_update = false
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager and mission_manager.current_mission:
if mission_manager.current_mission.id == "3" and structure.type == Structure.StructureType.RESIDENTIAL_BUILDING:
# Only update population for one resident, since we're removing them one by one
# We don't do total reset based on structure.population_count
skip_population_update = true
# We decrement by 1 in builder._remove_resident_for_building instead
if !skip_population_update:
total_population = max(0, total_population - structure.population_count)
# Update electricity
total_kW_usage = max(0, total_kW_usage - structure.kW_usage)
total_kW_production = max(0, total_kW_production - structure.kW_production)
# Update HUD
update_hud()
# Emit signals
electricity_updated.emit(total_kW_usage, total_kW_production)
# Update Population
func set_population_count(count: int):
total_population += count
population_label.text = str(total_population)
# # Emit signal
# increased_population.emit(added_population)
# Updates the HUD elements
func update_hud():
# Update population label
if population_label:
population_label.text = str(total_population)
# Update electricity label and indicator
if electricity_label:
# Default to red for the electricity indicator
var indicator_color = Color(1, 0, 0) # Red
if total_kW_usage > 0:
# If we have usage, check if production meets or exceeds it
# Only set to green if we meet or exceed demand
if total_kW_production >= total_kW_usage:
indicator_color = Color(0, 1, 0) # Green
else:
# Not enough power - keep it red
indicator_color = Color(1, 0, 0) # Red
# Update electricity label text (hidden for now but kept for future use)
electricity_label.text = str(total_kW_usage) + "/" + str(total_kW_production) + " kW"
else:
# If no usage but we have production, show green
if total_kW_production > 0:
indicator_color = Color(0, 1, 0) # Green
electricity_label.text = "0/" + str(total_kW_production) + " kW"
else:
# No usage and no production - show neutral color (gray)
indicator_color = Color(0.7, 0.7, 0.7) # Gray
electricity_label.text = "0/0 kW"
# Hide the text label for now, but keep implementation for later
electricity_label.visible = false
# Update the color of the indicator rectangle
if electricity_indicator:
electricity_indicator.color = indicator_color
# Tooltip handling
func _on_population_icon_mouse_entered():
if population_tooltip:
population_tooltip.visible = true
func _on_population_icon_mouse_exited():
if population_tooltip:
population_tooltip.visible = false
func _on_electricity_icon_mouse_entered():
if electricity_tooltip:
electricity_tooltip.visible = true
func _on_electricity_icon_mouse_exited():
if electricity_tooltip:
electricity_tooltip.visible = false
# Called when the sound button is pressed
func _on_sound_button_pressed():
# Consume the event to prevent click-through to the world
get_viewport().set_input_as_handled()
if sound_panel:
sound_panel.show_panel()
# Called when the help button is pressed
func _on_help_button_pressed():
# Consume the event to prevent click-through to the world
get_viewport().set_input_as_handled()
if controls_panel:
controls_panel.show_panel()
# Update money display
func update_money(amount: float):
current_money = amount
if cash_label:
cash_label.text = "$" + str(int(amount))
# Update energy balance display
func update_energy_balance(production: float, consumption: float):
total_kW_production = production
total_kW_usage = consumption
update_hud()

@ -0,0 +1,244 @@
extends Node
# Signals
signal electricity_updated(usage, production)
# Variables
var total_population: int = 0
var total_kW_usage: float = 0.0
var total_kW_production: float = 0.0
var current_money: float = 0.0
# References
var buildeJuj
var building_construction_manager
var population_label: Label
var electricity_label: Label
var electricity_indicator: ColorRect
var population_tooltip: Control
var electricity_tooltip: Control
var controls_panel: PanelContainer
var sound_panel: PanelContainer
<<<<<<< Updated upstream
var builder:Node
=======
var cash_label: Label
>>>>>>> Stashed changes
func _ready():
# Connect to signals from the builder
builder = get_node_or_null("/root/Main/Builder")
if builder:
builder.structure_placed.connect(_on_structure_placed)
builder.structure_removed.connect(_on_structure_removed)
# EventBus.population_update.connect(set_population_count)
# Initialize UI elements
population_label = $HBoxContainer/PopulationItem/PopulationLabel
if not population_label:
push_error("Population label not found in HUD!")
return
electricity_label = $HBoxContainer/ElectricityItem/ElectricityContainer/ElectricityValues/ElectricityLabel
if not electricity_label:
push_error("Electricity label not found in HUD!")
return
electricity_indicator = $HBoxContainer/ElectricityItem/ElectricityContainer/ElectricityValues/ElectricityIndicator
if not electricity_indicator:
push_error("Electricity indicator not found in HUD!")
return
population_tooltip = $PopulationTooltip
if not population_tooltip:
push_error("Population tooltip not found in HUD!")
return
electricity_tooltip = $ElectricityTooltip
<<<<<<< Updated upstream
=======
if not electricity_tooltip:
push_error("Electricity tooltip not found in HUD!")
return
cash_label = $HBoxContainer/CashItem/CashLabel
if not cash_label:
push_error("Cash label not found in HUD!")
return
>>>>>>> Stashed changes
# Ensure electricity indicator starts with red color
electricity_indicator.color = Color(1, 0, 0) # Start with red
# Hide the electricity label for now (keeping implementation for later)
electricity_label.visible = false
# Set tooltips
population_tooltip.visible = false
electricity_tooltip.visible = false
# Update HUD
update_hud()
func _process(delta):
# Update the population label if it changes
if population_label and Globals.population != total_population:
total_population = Globals.population
population_label.text = str(total_population)
# Called when a structure is placed
func _on_structure_placed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
return
var structure = builder.structures[structure_index]
# Only update population for non-residential buildings or if we're NOT in the construction mission
var is_residential = structure.type == Structure.StructureType.RESIDENTIAL_BUILDING
var mission_manager = get_node_or_null("/root/Main/MissionManager")
var using_construction = false
if mission_manager and mission_manager.current_mission:
var mission_id = mission_manager.current_mission.id
using_construction = (mission_id == "3" or mission_id == "1")
# Always update electricity usage/production
total_kW_usage += structure.kW_usage
total_kW_production += structure.kW_production
# Update HUD
update_hud()
# Emit signals
electricity_updated.emit(total_kW_usage, total_kW_production)
# Called when a structure is removed
func _on_structure_removed(structure_index, position):
if !builder or structure_index < 0 or structure_index >= builder.structures.size():
return
var structure = builder.structures[structure_index]
# Update population (but only for non-residential buildings in mission 3)
# For residential buildings in mission 3, we handle population separately in builder._remove_resident_for_building
var skip_population_update = false
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager and mission_manager.current_mission:
if mission_manager.current_mission.id == "3" and structure.type == Structure.StructureType.RESIDENTIAL_BUILDING:
# Only update population for one resident, since we're removing them one by one
# We don't do total reset based on structure.population_count
skip_population_update = true
# We decrement by 1 in builder._remove_resident_for_building instead
if !skip_population_update:
total_population = max(0, total_population - structure.population_count)
# Update electricity
total_kW_usage = max(0, total_kW_usage - structure.kW_usage)
total_kW_production = max(0, total_kW_production - structure.kW_production)
# Update HUD
update_hud()
# Emit signals
electricity_updated.emit(total_kW_usage, total_kW_production)
# Update Population
func set_population_count(count: int):
total_population += count
population_label.text = str(total_population)
# # Emit signal
# increased_population.emit(added_population)
# Updates the HUD elements
func update_hud():
# Update population label
if population_label:
population_label.text = str(total_population)
# Update electricity label and indicator
if electricity_label:
# Default to red for the electricity indicator
var indicator_color = Color(1, 0, 0) # Red
if total_kW_usage > 0:
# If we have usage, check if production meets or exceeds it
# Only set to green if we meet or exceed demand
if total_kW_production >= total_kW_usage:
indicator_color = Color(0, 1, 0) # Green
else:
# Not enough power - keep it red
indicator_color = Color(1, 0, 0) # Red
# Update electricity label text (hidden for now but kept for future use)
electricity_label.text = str(total_kW_usage) + "/" + str(total_kW_production) + " kW"
else:
# If no usage but we have production, show green
if total_kW_production > 0:
indicator_color = Color(0, 1, 0) # Green
electricity_label.text = "0/" + str(total_kW_production) + " kW"
else:
# No usage and no production - show neutral color (gray)
indicator_color = Color(0.7, 0.7, 0.7) # Gray
electricity_label.text = "0/0 kW"
# Hide the text label for now, but keep implementation for later
electricity_label.visible = false
# Update the color of the indicator rectangle
if electricity_indicator:
electricity_indicator.color = indicator_color
# Tooltip handling
func _on_population_icon_mouse_entered():
if population_tooltip:
population_tooltip.visible = true
func _on_population_icon_mouse_exited():
if population_tooltip:
population_tooltip.visible = false
func _on_electricity_icon_mouse_entered():
if electricity_tooltip:
electricity_tooltip.visible = true
func _on_electricity_icon_mouse_exited():
if electricity_tooltip:
electricity_tooltip.visible = false
# Called when the sound button is pressed
func _on_sound_button_pressed():
# Consume the event to prevent click-through to the world
get_viewport().set_input_as_handled()
if sound_panel:
sound_panel.show_panel()
# Called when the help button is pressed
func _on_help_button_pressed():
# Consume the event to prevent click-through to the world
get_viewport().set_input_as_handled()
if controls_panel:
controls_panel.show_panel()
# Update money display
func update_money(amount: float):
current_money = amount
if cash_label:
cash_label.text = "$" + str(int(amount))
# Update energy balance display
func update_energy_balance(production: float, consumption: float):
total_kW_production = production
total_kW_usage = consumption
update_hud()

@ -1,223 +1,187 @@
extends Node
class_name JSBridge
# Make this a singleton
static var instance = null
# Signals
signal init_data_received(data)
signal init_check_received
signal mission_progress_updated(data)
signal mission_completed(data)
signal open_react_graph(data)
signal open_react_table(data)
# Variables
static var _interface = null
var _init_data = null
var _init_check_received = false
static var _pending_signals = []
var _my_js_callback = JavaScriptBridge.create_callback(receive_init_data)
var window = null
func _init():
instance = self
# This script provides a bridge to JavaScript functionality
# while gracefully handling platforms that don't support it
# Check if JavaScript is available
static func has_interface() -> bool:
# Check if running in a web environment
# Use OS.has_feature("web") for consistency with sound_manager.gd
func _ready():
# Connect signals when the node is initialized
if OS.has_feature("web"):
print("Running in web environment, JavaScript should be available")
window = JavaScriptBridge.get_interface("window")
# Wait for the interface to be available
await get_tree().process_frame
receive_init_data("test")
# Set up message listener and interface
# Double-check by evaluating a simple script
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
var test_result = js.eval("!!window && typeof window !== 'undefined'")
print("JavaScript test result: " + str(test_result))
return test_result != null
else:
print("JavaScriptBridge singleton not available, running in editor or non-web platform")
else:
print("Not running in web environment")
# Set up the interface
_interface = JavaScript.get_interface()
print("JavaScript interface initialized")
# Set up callbacks in JavaScript
JavaScriptBridge.eval("""
window.godot_interface._callbacks.init_data_received = function(data) {
console.log('Emitting init_data_received signal with data:', data);
// Don't re-emit the signal to avoid recursion
window.godot_interface._callbacks.init_data_received = null;
};
window.godot_interface._callbacks.init_check_received = function() {
console.log('Emitting init_check_received signal');
// Don't re-emit the signal to avoid recursion
window.godot_interface._callbacks.init_check_received = null;
};
window.godot_interface._callbacks.mission_progress_updated = function(data) {
window.godot_interface.emit_signal('mission_progress_updated', data);
};
window.godot_interface._callbacks.mission_completed = function(data) {
window.godot_interface.emit_signal('mission_completed', data);
};
window.godot_interface._callbacks.open_react_graph = function(data) {
window.godot_interface.emit_signal('open_react_graph', data);
};
window.godot_interface._callbacks.open_react_table = function(data) {
window.godot_interface.emit_signal('open_react_table', data);
};
""")
print("JavaScript callbacks set up")
# Send init data from URL parameters if present
# Process any pending signals
_process_pending_signals()
func receive_init_data(missions_data):
# First, let's properly log the missions_data itself
print("Received missions data:", JSON.stringify(missions_data))
# To get URL information, we need to extract specific properties from window.location
if window and window.location:
# Access specific properties of the location object
var href = JavaScriptBridge.eval("window.location.href")
var search = JavaScriptBridge.eval("window.location.search")
var hostname = JavaScriptBridge.eval("window.location.hostname")
print("URL href:", href)
print("URL search params:", search)
print("URL hostname:", hostname)
# Parse URL parameters more directly
var params_str = JavaScriptBridge.eval("""
(function() {
var result = {};
var params = new URLSearchParams(window.location.search);
params.forEach(function(value, key) {
result[key] = value;
});
return JSON.stringify(result);
})()
""")
# Parse the JSON string to get a Dictionary
var params = JSON.parse_string(params_str)
print("URL parameters:", params)
# Now access and process the missions parameter if it exists
if params and "missions" in params:
var missions_json = params["missions"]
var missions = JSON.parse_string(missions_json)
print("Missions from URL:", missions)
var mission_data = {"missions": missions}
Globals.receive_data_from_browser(mission_data)
# Also process the directly passed missions_data
if missions_data and missions_data != "test":
print("Processing passed missions data")
emit_signal("init_data_received", missions_data)
return false
func _process_pending_signals():
if _pending_signals.size() > 0:
print("Processing pending signals...")
for signal_data in _pending_signals:
emit_signal(signal_data.signal_name, signal_data.data)
_pending_signals.clear()
print("All pending signals processed")
# Static methods for interface checks
static func has_interface() -> bool:
return JavaScript.has_interface()
# Get the JavaScript interface
static func get_interface():
if has_interface():
return JavaScriptGlobal
return null
# JavaScriptGlobal is a mock class that provides fallback implementations
# for platforms that don't support JavaScript
class JavaScriptGlobal:
# Check if a JavaScript function exists
static func has_function(function_name: String) -> bool:
if not OS.has_feature("web"):
return false
print("Checking if function exists: " + function_name)
var script = "typeof %s === 'function'" % function_name
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
var result = js.eval(script)
# If result is null, the JavaScript eval failed
if result == null:
print("JavaScript eval failed when checking for function: " + function_name)
return false
print("Function check result for " + function_name + ": " + str(result))
return result
else:
print("JavaScriptBridge singleton not available")
return false
# Evaluate JavaScript code
static func eval(script: String):
if not OS.has_feature("web"):
return null
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
return js.eval(script)
else:
print("JavaScriptBridge singleton not available")
return null
# Call a JavaScript function with arguments
static func call_js_function(function_name: String, args = []):
if not OS.has_feature("web"):
return null
var formatted_args = []
for arg in args:
if arg is String:
formatted_args.append("\"%s\"" % arg.replace("\"", "\\\""))
elif arg is Dictionary or arg is Array:
formatted_args.append(JSON.stringify(arg))
else:
formatted_args.append(str(arg))
var script = "%s(%s)" % [function_name, ",".join(formatted_args)]
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
return js.eval(script)
else:
print("JavaScriptBridge singleton not available")
return null
# Connect to the learning companion - legacy method with postMessage fallback
static func connectLearningCompanion(success_callback = null, error_callback = null):
print("Attempting to connect to learning companion")
if not OS.has_feature("web"):
print("Skipping learning companion connection on non-web platform")
if error_callback != null and error_callback.is_valid():
error_callback.call()
return
# Always use postMessage approach regardless of function availability
connectLearningCompanionViaPostMessage(success_callback, error_callback)
return JavaScript.get_interface()
# Connect to the learning companion using only postMessage
static func connectLearningCompanionViaPostMessage(success_callback = null, error_callback = null):
print("Connecting to learning companion via postMessage")
if not OS.has_feature("web"):
print("Skipping learning companion connection on non-web platform")
if error_callback != null and error_callback.is_valid():
error_callback.call()
return
# Use postMessage approach exclusively - note: no return statements allowed in the script
var script = """
(function() {
try {
// Send a message directly to the parent window
if (window.parent) {
console.log('Sending connection message to parent window');
window.parent.postMessage({
type: 'stemCity_connect',
source: 'godot-game',
timestamp: Date.now()
}, '*');
// Set up a global event listener for responses if not already set up
if (!window._stemCityListenerInitialized) {
window._stemCityListenerInitialized = true;
window.addEventListener('message', function(event) {
console.log('Game received message:', event.data);
if (event.data && event.data.type === 'stemCity_connect_ack') {
console.log('Received connection acknowledgment from parent');
}
});
}
// Don't use return statements here - they're not allowed in top-level eval
var result = true;
} else {
console.log('No parent window found');
var result = false;
}
} catch (e) {
console.error('Error connecting via postMessage:', e);
var result = false;
}
})();
"""
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
# Always consider this a success - we'll use the force connection timer as backup
print("Sent connection message via postMessage")
# Try to ensure audio is initialized as well since we now have user interaction
JavaScriptGlobal.ensure_audio_initialized()
if success_callback != null and success_callback.is_valid():
success_callback.call()
else:
print("JavaScriptBridge singleton not available")
if error_callback != null and error_callback.is_valid():
error_callback.call()
# The following methods call the JavaScript functions for game events using postMessage
# Helper method to convert Godot Dictionary to JSON string
static func JSON_stringify(data) -> String:
return JSON.stringify(data)
#static func send_signal(signal_name: String, data = null):
# if JavaScript.has_interface():
# if _interface and _interface.has_method("emit_signal"):
# _interface.emit_signal(signal_name, data)
# else:
# _pending_signals.append({"signal_name": signal_name, "data": data})
# else:
# _pending_signals.append({"signal_name": signal_name, "data": data})
#func send_mission_progress(mission_id: String, objective_index: int, current_count: int, target_count: int):
# send_signal("mission_progress_updated", {
# "mission_id": mission_id,
# "objective_index": objective_index,
# "current_count": current_count,
# "target_count": target_count
# })
#
#func send_mission_completed(mission_id: String):
# send_signal("mission_completed", {
# "mission_id": mission_id
# })
static func send_open_graph(graph_data: Dictionary):
# Format the message to match what React expects
var message = {
"source": "godot-game",
"type": "open_react_graph",
"data": graph_data
}
static func onGameStarted():
if not OS.has_feature("web"):
return
print("Sending game started event via postMessage")
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending gameStarted message to parent window');
window.parent.postMessage({
type: 'stemCity_gameStarted',
source: 'godot-game',
timestamp: Date.now()
}, '*');
} else {
console.log('No parent window found for gameStarted event');
}
} catch (e) {
console.error('Error sending gameStarted via postMessage:', e);
}
})();
"""
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
static func onMissionStarted(mission_data: Dictionary):
if not OS.has_feature("web"):
return
print("Sending mission started event for mission: " + str(mission_data.get("id", "unknown")))
var mission_json = JSON.stringify(mission_data)
# First, emit the Godot signal for internal listeners
# Then, send the message directly to React via postMessage for external listeners
if OS.has_feature("web"):
# send_signal("open_react_graph", graph_data)
var message_json = JSON_stringify(message)
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending missionStarted message to parent window');
window.parent.postMessage({
type: 'stemCity_missionStarted',
type: 'open_react_graph',
data: %s,
source: 'godot-game',
timestamp: Date.now()
@ -229,312 +193,82 @@ class JavaScriptGlobal:
console.error('Error sending missionStarted via postMessage:', e);
}
})();
""" % mission_json
# Use Engine.get_singleton for consistency with sound_manager.gd
""" % message_json
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
static func onMissionCompleted(mission_data: Dictionary):
if not OS.has_feature("web"):
return
print("Sending mission completed event for mission: " + str(mission_data.get("id", "unknown")))
var mission_json = JSON.stringify(mission_data)
else:
# For non-web platforms, add to pending signals
_pending_signals.append({"signal_name": "open_react_graph", "data": graph_data})
static func send_open_table(table_data: Dictionary):
# Format the message to match what React expects
var message = {
"source": "godot-game",
"type": "open_react_table",
"data": table_data
}
# First, emit the Godot signal for internal listeners
# emit_signal("open_react_table", table_data)
# Then, send the message directly to React via postMessage for external listeners
if OS.has_feature("web"):
var message_json = JSON_stringify(message)
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending missionCompleted message to parent window');
console.log('Sending missionStarted message to parent window');
window.parent.postMessage({
type: 'stemCity_missionCompleted',
type: 'stemCity_missionStarted',
data: %s,
source: 'godot-game',
timestamp: Date.now()
}, '*');
} else {
console.log('No parent window found for missionCompleted event');
console.log('No parent window found for missionStarted event');
}
} catch (e) {
console.error('Error sending missionCompleted via postMessage:', e);
console.error('Error sending missionStarted via postMessage:', e);
}
})();
""" % mission_json
""" % message_json
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
static func onAllMissionsCompleted():
if not OS.has_feature("web"):
return
print("Sending all missions completed event")
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending allMissionsCompleted message to parent window');
window.parent.postMessage({
type: 'stemCity_allMissionsCompleted',
source: 'godot-game',
timestamp: Date.now()
}, '*');
} else {
console.log('No parent window found for allMissionsCompleted event');
}
} catch (e) {
console.error('Error sending allMissionsCompleted via postMessage:', e);
}
})();
"""
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
static func sendCompanionDialog(key: String, dialog_data: Dictionary):
if not OS.has_feature("web"):
return
print("Sending companion dialog for key: " + key)
var dialog_json = JSON.stringify(dialog_data)
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending companion dialog to parent window');
window.parent.postMessage({
type: 'stemCity_companionDialog',
key: '%s',
data: %s,
source: 'godot-game',
timestamp: Date.now()
}, '*');
} else {
console.log('No parent window found for companion dialog');
}
} catch (e) {
console.error('Error sending companion dialog via postMessage:', e);
}
})();
""" % [key, dialog_json]
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
else:
# For non-web platforms, add to pending signals
_pending_signals.append({"signal_name": "open_react_table", "data": table_data})
# Handle audio actions via JavaScript
static func handle_audio_action(action: String, sound_name: String = "", volume: float = -1.0):
if not OS.has_feature("web"):
return false
print("Handling audio action via JavaScript bridge: " + action)
var action_data = {
"action": action,
"sound": sound_name,
}
if volume >= 0.0:
action_data["volume"] = volume
var action_json = JSON.stringify(action_data)
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending audio action to parent window:', %s);
window.parent.postMessage({
type: 'stemCity_audio',
data: %s,
source: 'godot-game',
timestamp: Date.now()
}, '*');
return true;
} else {
console.log('No parent window found for audio action');
return false;
}
} catch (e) {
console.error('Error sending audio action via postMessage:', e);
return false;
}
})();
""" % [action_json, action_json]
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
return js.eval(script)
else:
print("JavaScriptBridge singleton not available")
return false
# Helper method to ensure the sound manager's audio is initialized
# Call this method after user interaction to ensure audio works
static func ensure_audio_initialized():
if not OS.has_feature("web"):
return true # Audio always works on non-web platforms
print("Ensuring audio is initialized via JavaScript bridge")
# Setup audio message listener if it's not already set up
setup_audio_message_listener()
# Try to initialize audio through the sound manager if it exists
var sound_manager = _get_sound_manager()
if sound_manager and sound_manager.has_method("init_web_audio_from_js"):
print("Found SoundManager, calling init_web_audio_from_js")
sound_manager.init_web_audio_from_js()
# Follow up with direct JavaScript audio context unlocking for extra reliability
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
_run_audio_unlock_script(js)
return true
else:
# Fallback: directly try to unlock web audio using JavaScript
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
var result = _run_audio_unlock_script(js)
return result
else:
print("JavaScriptBridge singleton not available for audio initialization")
return false
# Helper method to run the audio unlocking script with maximum compatibility
static func _run_audio_unlock_script(js_interface):
var script = """
(function() {
var result = false;
try {
// Simple approach to unlock audio
console.log('Running simplified audio unlock');
// Create audio context if needed
if (!window._godotAudioContext) {
window._godotAudioContext = new (window.AudioContext || window.webkitAudioContext)();
}
var audioCtx = window._godotAudioContext;
console.log('Audio context state:', audioCtx.state);
// Resume it (for Chrome/Safari)
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
// Play a short, quiet beep
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
gainNode.gain.value = 0.01; // Very quiet
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start(0);
oscillator.stop(0.1);
// Add event listeners for future interactions
['click', 'touchstart', 'touchend'].forEach(function(event) {
document.addEventListener(event, function() {
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
}, {once: false});
});
result = audioCtx.state === 'running';
} catch (e) {
console.error("JavaScript bridge: Audio unlock error:", e);
result = false;
}
return result;
})()
"""
# var result = js_interface.eval(script)
# print("JavaScript audio initialization result:", result)
# return result
# Setup audio message listener from JavaScript
static func setup_audio_message_listener():
if not OS.has_feature("web"):
return false
print("Setting up audio message listener via JavaScript bridge")
if not Engine.has_singleton("JavaScriptBridge"):
print("JavaScriptBridge singleton not available")
return false
var js = Engine.get_singleton("JavaScriptBridge")
# Register the callback function
js.set_callback("godot_audio_callback", Callable(_get_sound_manager(), "process_js_audio_state"))
# Set up a listener for audio state messages
var script = """
(function() {
// Set up message listener if not already done
if (!window.godot_audio_listener_initialized) {
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'stemCity_audio_state') {
console.log('Godot received audio state:', event.data);
// Call our Godot callback with the state data
if (typeof godot_audio_callback === 'function') {
console.log('Sending audio state to Godot');
godot_audio_callback(event.data.data);
} else {
console.warn('godot_audio_callback is not available');
}
}
});
console.log('Audio message listener initialized');
window.godot_audio_listener_initialized = true;
// Request initial audio state from parent
window.parent.postMessage({
type: 'stemCity_audio',
data: { action: 'GET_STATE' },
source: 'godot-game',
timestamp: Date.now()
}, '*');
return true;
}
return false;
})();
"""
var result = js.eval(script)
print("Audio message listener setup result: ", result)
return result
# Helper to get the sound manager instance
static func _get_sound_manager():
var sound_manager = null
# Try to find using meta
if Engine.get_main_loop().has_meta("sound_manager"):
sound_manager = Engine.get_main_loop().get_meta("sound_manager")
else:
# Try to find in scene tree
var scene_tree = Engine.get_main_loop() as SceneTree
if scene_tree:
sound_manager = scene_tree.root.get_node_or_null("/root/SoundManager")
return sound_manager
#func send_companion_dialog(dialog_type: String, dialog_data: Dictionary):
# send_signal("companion_dialog", {
# "type": dialog_type,
# "data": dialog_data
# })
#
#func send_audio_action(action: String, data: Dictionary = {}):
# send_signal("audio_action", {
# "action": action,
# "data": data
# })
# Static wrappers so send_open_ functions can be called on the class directly
#static func send_open_graph(graph_data: Dictionary) -> void:
# if instance:
# instance.send_open_graph(graph_data)
# else:
# push_error("JavaScriptBridge not initialized; cannot send_open_graph")
#static func send_open_table(table_data: Dictionary) -> void:
# if instance:
# instance.send_open_table(table_data)
# else:
# push_error("JavaScriptBridge not initialized; cannot send_open_table")

@ -0,0 +1,93 @@
extends Node
# JavaScript global class for handling JavaScript functionality
class_name JavaScript
# Check if JavaScript is available
static func has_interface() -> bool:
# Check if running in a web environment
if OS.has_feature("web"):
print("Running in web environment, JavaScript should be available")
# Double-check by evaluating a simple script
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
var test_result = js.eval("!!window && typeof window !== 'undefined'")
print("JavaScript test result: " + str(test_result))
return test_result != null
else:
print("JavaScriptBridge singleton not available, running in editor or non-web platform")
else:
print("Not running in web environment")
return false
# Get the JavaScript interface
static func get_interface():
if has_interface():
return JavaScriptGlobal
return null
# JavaScriptGlobal is a mock class that provides fallback implementations
# for platforms that don't support JavaScript
class JavaScriptGlobal:
# Check if a JavaScript function exists
static func has_function(function_name: String) -> bool:
if not OS.has_feature("web"):
return false
print("Checking if function exists: " + function_name)
var script = "typeof %s === 'function'" % function_name
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
var result = js.eval(script)
# If result is null, the JavaScript eval failed
if result == null:
print("JavaScript eval failed when checking for function: " + function_name)
return false
print("Function check result for " + function_name + ": " + str(result))
return result
else:
print("JavaScriptBridge singleton not available")
return false
# Evaluate JavaScript code
static func eval(script: String):
if not OS.has_feature("web"):
return null
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
return js.eval(script)
else:
print("JavaScriptBridge singleton not available")
return null
# Call a JavaScript function with arguments
static func call_js_function(function_name: String, args = []):
if not OS.has_feature("web"):
return null
var formatted_args = []
for arg in args:
if arg is String:
formatted_args.append("\"%s\"" % arg.replace("\"", "\\\""))
elif arg is Dictionary or arg is Array:
formatted_args.append(JSON.stringify(arg))
else:
formatted_args.append(str(arg))
var script = "%s(%s)" % [function_name, ",".join(formatted_args)]
# Use Engine.get_singleton for consistency with sound_manager.gd
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
return js.eval(script)
else:
print("JavaScriptBridge singleton not available")
return null

@ -0,0 +1 @@
uid://boq8j7e7veaup

@ -0,0 +1 @@
uid://djghkkucobwfl

@ -0,0 +1 @@
uid://s05virx2cxsi

@ -12,6 +12,7 @@ signal worker_construction_ended
const CONSTRUCTION_TIME = 10.0 # seconds to build a building
var debug_timer = 0.0 # Add debug timer
# References to necessary scenes and resources
@ -19,6 +20,8 @@ var worker_scene: PackedScene
var hud_manager: Node
var nav_region: NavigationRegion3D
var builder: Node3D
var gridmap: GridMap
var structures: Array[Structure]
var building_plot_scene: PackedScene
var final_building_scene: PackedScene
var mission_manager: MissionManager
@ -27,12 +30,23 @@ var mission_manager: MissionManager
var construction_sites = {} # position (Vector3) -> construction data (dict)
func _ready():
print("\n=== Initializing Building Construction Manager ===")
# Load the worker character scene - add more fallbacks to ensure we get a valid model
builder = get_node_or_null('/root/Main/Builder')
worker_scene = load("res://people/character-male-a.glb")
hud_manager = get_node_or_null("/root/Main/CanvasLayer/HUD")
mission_manager = builder.get_node_or_null("/root/Main/MissionManager")
# Get navigation region
if builder and builder.nav_region:
nav_region = builder.nav_region
print("Found navigation region with ", nav_region.get_child_count(), " children")
for child in nav_region.get_children():
print("Nav region child: ", child.name)
else:
print("WARNING: No navigation region found!")
if not worker_scene:
worker_scene = load("res://people/character-female-a.glb")
if not worker_scene:
@ -52,10 +66,17 @@ func _ready():
if not final_building_scene:
# Create an empty PackedScene as a last resort
final_building_scene = PackedScene.new()
print("=== Building Construction Manager Initialized ===\n")
# Call this method to start construction at a position
func start_construction(position: Vector3, structure_index: int, rotation_basis = null):
print("\n=== Starting Construction ===")
print("Position: ", position)
print("Structure Index: ", structure_index)
if position in construction_sites:
print("ERROR: Construction site already exists at this position!")
return
# Get the current selector rotation if available
@ -68,6 +89,8 @@ func start_construction(position: Vector3, structure_index: int, rotation_basis
if builder.gridmap:
rotation_index = builder.gridmap.get_orthogonal_index_from_basis(rotation_basis)
print("Rotation Index: ", rotation_index)
# Create a construction site entry
construction_sites[position] = {
"position": position,
@ -76,10 +99,12 @@ func start_construction(position: Vector3, structure_index: int, rotation_basis
"worker": null,
"timer": 0.0,
"completed": false,
"rotation_index": rotation_index, # Store the rotation for later use
"rotation_basis": rotation_basis # Store the rotation basis
"rotation_index": rotation_index,
"rotation_basis": rotation_basis
}
print("Created construction site entry")
# Place plot marker (outline/transparent version of the building)
var plot
@ -87,14 +112,17 @@ func start_construction(position: Vector3, structure_index: int, rotation_basis
if structure_index >= 0 and structure_index < builder.structures.size():
var structure = builder.structures[structure_index]
plot = structure.model.instantiate()
print("Created plot using structure model: ", structure.model.resource_path)
else:
# Fallback to default building model
plot = building_plot_scene.instantiate()
print("Created plot using fallback model")
plot.name = "Plot_" + str(int(position.x)) + "_" + str(int(position.z))
# Make it a transparent outline by applying transparency to all materials
_make_model_transparent(plot, 0.3) # More transparent (0.3 instead of 0.5)
_make_model_transparent(plot, 0.3)
print("Applied transparent material to plot")
# Add to the scene and position it
builder.add_child(plot)
@ -104,34 +132,17 @@ func start_construction(position: Vector3, structure_index: int, rotation_basis
if rotation_basis:
plot.basis = rotation_basis
plot.scale = Vector3(3.0, 3.0, 3.0) # Scale to match other buildings
plot.scale = Vector3(3.0, 3.0, 3.0)
print("Added plot to scene at position: ", position)
# Store reference
construction_sites[position]["plot"] = plot
# Check if we should spawn a worker based on structure type
var should_spawn_worker = false
if structure_index >= 0 and structure_index < builder.structures.size():
var structure = builder.structures[structure_index]
# Check if this structure type should spawn a builder
if structure.spawn_builder:
should_spawn_worker = true
else:
# Default to true if we can't determine the structure type
should_spawn_worker = true
# Find a road position to spawn the worker if needed
if should_spawn_worker:
_spawn_worker_for_construction(position)
else:
# For structures without workers, start a timer to complete construction automatically
var timer = get_tree().create_timer(CONSTRUCTION_TIME)
timer.timeout.connect(func(): _complete_construction(position))
# Always spawn a worker for construction
_spawn_worker_for_construction(position)
print("=== Construction Started ===\n")
# Send building_selected dialog to learning companion if available in the current mission
var mission_manager = builder.get_node_or_null("/root/Main/MissionManager")
if mission_manager and mission_manager.current_mission and mission_manager.learning_companion_connected:
var mission = mission_manager.current_mission
@ -154,6 +165,7 @@ func start_construction(position: Vector3, structure_index: int, rotation_basis
# Process active construction sites
func _process(delta):
var sites_to_complete = []
debug_timer += delta # Track total time
# Update all construction sites
for pos in construction_sites.keys():
@ -162,44 +174,77 @@ func _process(delta):
# Skip completed sites
if site["completed"]:
continue
# Get the structure's build time
var build_time = CONSTRUCTION_TIME # Default fallback
if site["structure_index"] >= 0 and site["structure_index"] < builder.structures.size():
var structure = builder.structures[site["structure_index"]]
if "build_time" in structure:
build_time = structure.build_time
# Update timer for all active sites, regardless of worker status
site["timer"] += delta
# Update the construction preview shader progress
if site["plot"] != null:
var progress = site["timer"] / build_time
progress = clamp(progress, 0.0, 1.0)
# Update timer for active sites
if site["worker"] != null and site["worker"].is_construction_active:
site["timer"] += delta
# Find all mesh instances and update their materials
var mesh_instances = []
_find_all_mesh_instances(site["plot"], mesh_instances)
for mesh_instance in mesh_instances:
for i in range(mesh_instance.get_surface_override_material_count()):
var material = mesh_instance.get_surface_override_material(i)
if material and material is ShaderMaterial:
material.set_shader_parameter("progress", progress)
material.set_shader_parameter("alpha", 0.3)
# Check if construction is complete
if site["timer"] >= CONSTRUCTION_TIME:
sites_to_complete.append(pos)
# Check if construction is complete
if site["timer"] >= build_time:
sites_to_complete.append(pos)
# Complete construction for sites that are done
for pos in sites_to_complete:
print("\n=== Completing Construction ===")
print("Position: ", pos)
_complete_construction(pos)
# Find a road and spawn a worker there
func _spawn_worker_for_construction(target_position: Vector3):
print("\n=== Attempting to Spawn Worker ===")
print("Target Position: ", target_position)
# Find closest road tile
var road_position = _find_nearest_road(target_position)
if road_position == Vector3.ZERO:
print("ERROR: No road found for worker spawn at target position: ", target_position)
# Force completion immediately without worker since we can't spawn one
var timer = get_tree().create_timer(0.5)
timer.timeout.connect(func(): _complete_construction(target_position))
print("Checking navigation region...")
if nav_region:
print("Nav region has ", nav_region.get_child_count(), " children")
for child in nav_region.get_children():
print("Child: ", child.name)
else:
print("Nav region is null!")
# Don't force completion - let the timer run normally
return
print("DEBUG: Spawning worker at road position: ", road_position, " for target: ", target_position)
print("Found road position: ", road_position)
print("Distance to target: ", road_position.distance_to(target_position))
# Create the worker
var worker = _create_worker(road_position, target_position)
if worker == null:
print("ERROR: Failed to create worker for construction at: ", target_position)
# Force completion immediately without worker
var timer = get_tree().create_timer(0.5)
timer.timeout.connect(func(): _complete_construction(target_position))
# Don't force completion - let the timer run normally
return
# Store in the construction site data
construction_sites[target_position]["worker"] = worker
print("Worker spawned successfully")
print("=== Worker Spawn Complete ===\n")
# Find nearest road (simplified version of what's in mission_manager.gd)
func _find_nearest_road(position: Vector3) -> Vector3:
@ -296,6 +341,7 @@ func _on_worker_construction_started():
func _on_worker_construction_ended():
# Forward the signal for mission managers/other systems that need it
worker_construction_ended.emit()
func update_population(count: int):
Globals.set_population_count(count)
@ -304,21 +350,34 @@ func update_population(count: int):
# Complete construction at a position
func _complete_construction(position: Vector3):
print("\n=== Completing Construction Process ===")
print("Position: ", position)
if not position in construction_sites:
print("ERROR: No construction site found at position!")
return
var site = construction_sites[position]
print("Found construction site")
# Verify the timer has reached the build time
var build_time = CONSTRUCTION_TIME
if site["structure_index"] >= 0 and site["structure_index"] < builder.structures.size():
var structure = builder.structures[site["structure_index"]]
if "build_time" in structure:
build_time = structure.build_time
if site["timer"] < build_time:
print("WARNING: Construction completing before timer is done!")
return
# Mark as completed
site["completed"] = true
# Remove placeholder plot
if site["plot"] != null:
site["plot"].queue_free()
site["plot"] = null
print("Marked as completed")
# Stop worker and send back to a road
if site["worker"] != null:
print("Stopping worker")
if site["worker"].has_method("finish_construction"):
site["worker"].finish_construction()
else:
@ -326,32 +385,41 @@ func _complete_construction(position: Vector3):
site["worker"].queue_free()
site["worker"] = null
# Remove placeholder plot
if site["plot"] != null:
print("Removing plot")
site["plot"].queue_free()
site["plot"] = null
# Place the final building
print("Placing final building")
_place_final_building(position, site["structure_index"])
# Update mission objective now that construction is complete
# _update_mission_objective_on_completion(site["structure_index"])
# Check if we should spawn a resident (only for residential buildings)
var mission_manager = builder.get_node_or_null("/root/Main/MissionManager")
var should_spawn_resident = false
# Only spawn residents for residential buildings
var structure = builder.structures[site["structure_index"]]
if structure.type == Structure.StructureType.RESIDENTIAL_BUILDING:
should_spawn_resident = true
should_spawn_resident = true
# Spawn a resident from the new residential building if appropriate
if should_spawn_resident:
print("Spawning resident")
_spawn_resident_from_building(position)
if structure.type == Structure.StructureType.RESIDENTIAL_BUILDING and structure.population_count > 0:
update_population(structure.population_count)
#
update_population(structure.population_count)
# Emit completion signal
print("Emitting completion signal")
construction_completed.emit(position)
# Remove from construction sites
print("Removing from construction sites")
construction_sites.erase(position)
print("=== Construction Process Complete ===\n")
# Function to handle building demolition at a position
func handle_demolition(position: Vector3):
@ -375,13 +443,19 @@ func handle_demolition(position: Vector3):
# Place the final building at the construction site
func _place_final_building(position: Vector3, structure_index: int):
print("\n=== Placing Final Building ===")
print("Position: ", position)
print("Structure Index: ", structure_index)
# Create the final building using the actual selected structure model
var building
if structure_index >= 0 and structure_index < builder.structures.size():
building = builder.structures[structure_index].model.instantiate()
print("Created building using structure model: ", builder.structures[structure_index].model.resource_path)
else:
# Fallback to our default model if structure index is invalid
# Fallback to default building model
building = final_building_scene.instantiate()
print("Created building using fallback model")
building.name = "Building_" + str(int(position.x)) + "_" + str(int(position.z))
@ -395,10 +469,15 @@ func _place_final_building(position: Vector3, structure_index: int):
if "rotation_basis" in site and site["rotation_basis"]:
building.basis = site["rotation_basis"]
building.scale = Vector3(3.0, 3.0, 3.0) # Scale to match other buildings
building.scale = Vector3(3.0, 3.0, 3.0)
print("Added building to scene")
# Add to gridmap for collision detection and mission tracking
if builder.gridmap:
print("Adding to gridmap")
builder.gridmap.set_cell_item(position, structure_index, builder.gridmap.get_orthogonal_index_from_basis(building.basis))
# Send placement_success dialog to learning companion if available in the current mission
var mission_manager = builder.get_node_or_null("/root/Main/MissionManager")
if mission_manager and mission_manager.current_mission and mission_manager.learning_companion_connected:
var mission = mission_manager.current_mission
@ -422,42 +501,29 @@ func _place_final_building(position: Vector3, structure_index: int):
# Make a model semi-transparent with outline effect
func _make_model_transparent(model: Node3D, alpha: float):
# Load the construction preview shader material
var preview_material = load("res://models/Materials/construction_preview.tres")
if not preview_material:
push_error("Failed to load construction preview material")
return
# Find all mesh instances
var mesh_instances = []
_find_all_mesh_instances(model, mesh_instances)
# Adjust the materials for each mesh instance
# Apply the preview material to each mesh instance
for mesh_instance in mesh_instances:
var materials_count = mesh_instance.get_surface_override_material_count()
for i in range(materials_count):
var material = mesh_instance.get_surface_override_material(i)
if not material:
# If no override material, try to get the mesh material
if mesh_instance.mesh and i < mesh_instance.mesh.get_surface_count():
material = mesh_instance.mesh.surface_get_material(i)
# Clone the preview material to avoid affecting other instances
var new_material = preview_material.duplicate()
if material:
# Clone the material to avoid affecting other instances
var new_material = material.duplicate()
# Set up transparency
new_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
new_material.albedo_color.a = alpha
# Make it look more like an outline/hologram
new_material.emission_enabled = true
new_material.emission = Color(0.2, 0.5, 0.8, 1.0) # Light blue emission
new_material.emission_energy = 0.5
# Optional: add a subtle outline effect
if new_material is StandardMaterial3D:
new_material.outline_enabled = true
new_material.outline_width = 3.0
new_material.outline_color = Color(0.0, 0.6, 1.0)
# Apply modified material
mesh_instance.set_surface_override_material(i, new_material)
# Set the alpha value from the parameter
new_material.set_shader_parameter("alpha", alpha)
# Apply the material
mesh_instance.set_surface_override_material(i, new_material)
# Spawn a resident from a newly constructed building
func _spawn_resident_from_building(position: Vector3):

@ -1,38 +1,60 @@
extends Node
# Simple function to handle structure unlocking for missions
static func process_structures(structures, mission_id):
static func process_structures(structures, mission):
print("\n=== Processing Structures for Mission: ", mission.id, " ===")
print("Starting Structures: ", mission.starting_structures)
print("Unlocked Items: ", mission.unlocked_items)
for structure in structures:
print("\n--- Processing Structure: ", structure.model.resource_path, " ---")
print("Initial status: ", structure.unlocked)
# Default is locked
structure.unlocked = false
print("After default lock: ", structure.unlocked)
# Handle starting structures (always available for this mission)
if mission.starting_structures != null:
print("Checking starting structures: ", mission.starting_structures)
for item in mission.starting_structures:
if structure.model.resource_path == item or structure.resource_path == item:
print("Found matching starting structure: ", item)
structure.unlocked = true
print("After starting structures check: ", structure.unlocked)
break
# Handle basic structures (always available)
# Handle unlocked items (unlocked from previous missions)
if mission.unlocked_items != null:
print("Checking unlocked items: ", mission.unlocked_items)
for item in mission.unlocked_items:
if structure.model.resource_path == item or structure.resource_path == item:
print("Found matching unlocked item: ", item)
structure.unlocked = true
print("After unlocked items check: ", structure.unlocked)
break
# Check if it's a basic structure
if basic_structure(structure):
print("Structure is marked as basic")
structure.unlocked = true
print("After basic structure check: ", structure.unlocked)
# Handle mission-specific structures
if mission_id == "1":
# Mission 1: Unlock residential buildings
if residential_building(structure):
structure.unlocked = true
elif mission_id == "2" or mission_id == "3":
# Missions 2-3: Unlock curved roads and decoration
if curved_road(structure) or decoration(structure):
structure.unlocked = true
elif mission_id == "4" or mission_id == "5":
# Missions 4-5: Unlock power plants
if power_plant(structure):
structure.unlocked = true
print("Final status: ", structure.unlocked)
print("--- End Structure Processing ---\n")
# Simple helper functions to categorize structures
static func basic_structure(structure):
var path = structure.model.resource_path
return path.contains("road-straight") or path.contains("pavement")
# Only consider the exact road-straight.tres as basic
return path == "res://structures/road-straight.tres" or path.contains("pavement")
static func residential_building(structure):
return structure.model.resource_path.contains("building-small-a")
var path = structure.model.resource_path
var res_path = structure.resource_path if structure.has("resource_path") else ""
var is_residential = path.contains("building-small-a") or res_path.contains("building-small-a")
print("Checking if residential: ", path, " or ", res_path, " = ", is_residential)
return is_residential
static func curved_road(structure):
return structure.model.resource_path.contains("road-corner")

@ -5,17 +5,14 @@ extends Node
# Unlocks basic structures and locks special ones
func process_structures(structures_array):
# Start with all unlocked
# Start with all locked
for structure in structures_array:
# Basic structures are always unlocked
if basic_structure(structure):
structure.unlocked = true
# Special structures start locked unless mission-specific
elif special_structure(structure):
if special_structure(structure):
structure.unlocked = false
else:
# Default to unlocked for other structures
structure.unlocked = true
# Default to locked for other structures
structure.unlocked = false
# Check if this is a mission-dependent structure type that should be unlocked
func unlock_by_mission(structures_array, mission_id):
@ -35,13 +32,6 @@ func unlock_by_mission(structures_array, mission_id):
if structure.model.resource_path.contains("power_plant"):
structure.unlocked = true
# Check if a structure is one of the basic types (always available)
func basic_structure(structure):
return (structure.model.resource_path.contains("road-straight") or
structure.model.resource_path.contains("building-small-a") or
structure.model.resource_path.contains("pavement") or
structure.model.resource_path.contains("grass.glb"))
# Check if a structure is a special type (requires unlocking)
func special_structure(structure):
return (structure.model.resource_path.contains("road-corner") or
@ -59,8 +49,7 @@ func select_unlocked_structure(builder):
found_unlocked = true
break
# If no structures are unlocked, unlock a basic one
# If no structures are unlocked, use the first structure
if not found_unlocked and builder.structures.size() > 0:
builder.structures[0].unlocked = true
builder.index = 0
builder.update_structure()

@ -32,7 +32,7 @@ func _ready():
# Only get references needed for signal connections
user_input = get_node_or_null("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MainContent/UserInputContainer/UserInput")
submit_button = get_node_or_null("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/SubmitButtonContainer/SubmitButton")
submit_button = get_node_or_null("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MainContent/UserInputContainer/SubmitButton")
# Clear the user inputs array
user_inputs = []
@ -46,69 +46,73 @@ func _ready():
push_error("Submit button not found in learning panel")
func show_learning_panel(mission_data: MissionData):
# Check if the mission data is valid
if mission_data == null:
push_error("Invalid mission data provided to learning panel")
return
mission = mission_data
# Make panel fully visible and ensure process mode is set to handle input while paused
visible = true
process_mode = Node.PROCESS_MODE_ALWAYS
# First, reset the panel to a clean state
_reset_panel()
# Use traditional text and graph mode
_setup_traditional_mode()
# Set up the correct answer from mission data
if not mission.correct_answer.is_empty():
correct_answer = mission.correct_answer
else:
# Default answer based on mission type
correct_answer = "1" if not mission.power_math_content.is_empty() else "A"
# Set up user input fields based on mission data
if mission.num_of_user_inputs > 1:
_setup_multiple_user_inputs()
# Set focus to the first input field after a short delay
get_tree().create_timer(0.2).timeout.connect(func():
if user_inputs.size() > 0 and user_inputs[0]:
user_inputs[0].grab_focus()
print("Set focus to the first input field in multi-input")
)
else:
# Traditional single input
if user_input:
user_input.placeholder_text = mission.question_text if not mission.question_text.is_empty() else "Enter your answer"
# Set focus to the single input field after a short delay
get_tree().create_timer(0.2).timeout.connect(func():
user_input.grab_focus()
print("Set focus to the single input field")
)
# Hide the HUD when learning panel is shown
var hud = get_node_or_null("/root/Main/CanvasLayer/HUD")
if hud:
hud.visible = false
# Make sure we're on top
if get_parent():
get_parent().move_child(self, get_parent().get_child_count() - 1)
# Make sure we're at the proper z-index
z_index = 100
# Disable background interaction by creating a fullscreen invisible barrier
_disable_background_interaction()
# Emit signal to lock building controls
panel_opened.emit()
print("Panel is now visible = ", visible)
# Disabled learning panel
return
# Original code commented out
## Check if the mission data is valid
#if mission_data == null:
# push_error("Invalid mission data provided to learning panel")
# return
#
#mission = mission_data
#
## Make panel fully visible and ensure process mode is set to handle input while paused
#visible = true
#process_mode = Node.PROCESS_MODE_ALWAYS
#
## First, reset the panel to a clean state
#_reset_panel()
#
## Use traditional text and graph mode
#_setup_traditional_mode()
#
## Set up the correct answer from mission data
#if not mission.correct_answer.is_empty():
# correct_answer = mission.correct_answer
#else:
# # Default answer based on mission type
# correct_answer = "1" if not mission.power_math_content.is_empty() else "A"
#
## Set up user input fields based on mission data
#if mission.num_of_user_inputs > 1:
# _setup_multiple_user_inputs()
# # Set focus to the first input field after a short delay
# get_tree().create_timer(0.2).timeout.connect(func():
# if user_inputs.size() > 0 and user_inputs[0]:
# user_inputs[0].grab_focus()
# print("Set focus to the first input field in multi-input")
# )
#else:
# # Traditional single input
# if user_input:
# user_input.placeholder_text = mission.question_text if not mission.question_text.is_empty() else "Enter your answer"
# # Set focus to the single input field after a short delay
# get_tree().create_timer(0.2).timeout.connect(func():
# user_input.grab_focus()
# print("Set focus to the single input field")
# )
#
## Hide the HUD when learning panel is shown
#var hud = get_node_or_null("/root/Main/CanvasLayer/HUD")
#if hud:
# hud.visible = false
#
## Make sure we're on top
#if get_parent():
# get_parent().move_child(self, get_parent().get_child_count() - 1)
#
## Make sure we're at the proper z-index
#z_index = 100
#
## Disable background interaction by creating a fullscreen invisible barrier
#_disable_background_interaction()
#
## Emit signal to lock building controls
#panel_opened.emit()
#
#print("Panel is now visible = ", visible)
# Creates an invisible fullscreen barrier to block clicks on the background
func _disable_background_interaction():
# Remove any existing barrier

@ -270,4 +270,4 @@ func _on_complete_button_pressed():
hide_learning_panel()
# Emit signal
completed.emit()
completed.emit()

@ -0,0 +1 @@
uid://d1ndmeoogiuf4

@ -20,3 +20,11 @@ class_name MissionData
@export var input_labels: Array[String] = [] # Labels for each input field
@export var companion_dialog: Dictionary = {} # Map of event keys to dialog entries for the learning companion
@export var unlocked_items: Array[String] = [] # Array of structure resource paths that get unlocked after mission completion
@export var starting_structures: Array[String] = [] # Array of structure resource paths that are unlocked when this mission starts
@export var open_react_graph: bool = false # Whether to display the React graph component
@export var open_react_table: bool = false # Whether to display the React table component
@export var react_data: Dictionary
@export var react_table_data: Dictionary = {
"headers": [], # Array of header text strings
"rows": [] # 2D array of cell values: [[row1cell1, row1cell2...], [row2cell1, row2cell2...], ...]
} # Data structure for React table component

@ -0,0 +1,179 @@
extends Node
class_name MissionLoader
const MissionData = preload("res://scripts/mission/mission_data.gd")
const MissionObjective = preload("res://scripts/mission/mission_objective.gd")
var mission_manager: MissionManager
var builder: Node3D
func _init(manager: MissionManager, builder_ref: Node3D):
mission_manager = manager
builder = builder_ref
# Load mission data from JavaScript
func load_from_js(mission_data: Dictionary) -> void:
print("\n=== Loading Mission Data from JavaScript ===")
print("Received mission data:", mission_data)
if not mission_data:
print("WARNING: No mission data received from JavaScript")
return
print("Converting mission data to Godot objects...")
if "missions" in mission_data:
var missions = _convert_missions(mission_data.missions)
print("Converted missions:", missions)
print("Number of missions converted: ", missions.size())
if missions.is_empty():
print("WARNING: No missions were converted from the data")
return
print("Setting up missions...")
mission_manager.missions = missions
print("Missions set in manager. Current missions array size: ", mission_manager.missions.size())
if mission_manager.missions.size() > 0:
print("First mission details:")
print(" Title: ", mission_manager.missions[0].title)
print(" Description: ", mission_manager.missions[0].description)
print(" Number of objectives: ", mission_manager.missions[0].objectives.size())
print("=== Mission Data Loading Complete ===\n")
else:
print("WARNING: No 'missions' key found in mission data")
# Unlock the starting structures
func _unlock_starting_structures(structure_paths: Array) -> void:
print("\n=== Unlocking Starting Structures ===")
print("Paths to unlock: ", structure_paths)
if not builder:
push_error("Builder not available")
return
# Get current structures
var structures = builder.get_structures()
if not structures:
push_error("No structures available")
return
# Process each path
for path in structure_paths:
print("\nProcessing path: ", path)
# Handle both structures/ and models/ paths
var possible_paths = []
# Add the original path
possible_paths.append(path)
# Handle .tres to .glb conversion
if path.ends_with(".tres"):
# Convert structures/ path to models/ path
if "structures/" in path:
possible_paths.append(path.replace("structures/", "models/").replace(".tres", ".glb"))
# Handle .glb to .tres conversion
if path.ends_with(".glb"):
# Convert models/ path to structures/ path
if "models/" in path:
possible_paths.append(path.replace("models/", "structures/").replace(".glb", ".tres"))
print("Trying possible paths: ", possible_paths)
var found_match = false
for structure in structures:
if not structure.model:
continue
var structure_path = structure.model.resource_path
# Only try exact path matches
for possible_path in possible_paths:
if structure_path == possible_path:
structure.unlocked = true
found_match = true
break
if found_match:
break
if not found_match:
print("WARNING: No match found for path: ", path)
# Convert mission dictionaries to MissionData objects
func _convert_missions(mission_dicts: Array) -> Array[MissionData]:
print("Converting mission configurations...")
var converted: Array[MissionData] = []
for mission_dict in mission_dicts:
var mission_data = MissionData.new()
# Set basic properties
mission_data.id = mission_dict.get("id", "")
mission_data.title = mission_dict.get("title", "")
mission_data.description = mission_dict.get("description", "")
mission_data.open_react_graph = mission_dict.get("open_react_graph", false)
mission_data.open_react_table = mission_dict.get("open_react_table", false)
mission_data.react_data = mission_dict.get("react_data", {})
mission_data.react_table_data = mission_dict.get("react_table_data", {
"headers": [], # Array of header text strings
"rows": [] # 2D array of cell values: [[row1cell1, row1cell2...], [row2cell1, row2cell2...], ...]
})
# Convert objectives
var objectives: Array[MissionObjective] = []
for obj in mission_dict.get("objectives", []):
var objective = MissionObjective.new()
objective.type = obj.get("type", 0)
objective.target_count = obj.get("target_count", 0)
objective.description = obj.get("description", "")
# Load structure resource if specified
var structure_path = obj.get("structure_path", "")
if structure_path:
objective.structure = load(structure_path)
objectives.append(objective)
mission_data.objectives = objectives
# Set rewards
mission_data.rewards = mission_dict.get("rewards", {})
# Set unlocked items
var unlocked_items_array: Array[String] = []
for item in mission_dict.get("unlocked_items", []):
unlocked_items_array.append(str(item))
mission_data.unlocked_items = unlocked_items_array
# Set starting structures
var starting_structures_array: Array[String] = []
for item in mission_dict.get("starting_structures", []):
starting_structures_array.append(str(item))
mission_data.starting_structures = starting_structures_array
# Set additional properties
mission_data.next_mission_id = mission_dict.get("next_mission_id", "")
mission_data.graph_path = mission_dict.get("graph_path", "")
mission_data.full_screen_path = mission_dict.get("full_screen_path", "")
mission_data.intro_text = mission_dict.get("intro_text", "")
mission_data.question_text = mission_dict.get("question_text", "")
mission_data.correct_answer = mission_dict.get("correct_answer", "")
mission_data.feedback_text = mission_dict.get("feedback_text", "")
mission_data.incorrect_feedback = mission_dict.get("incorrect_feedback", "")
mission_data.company_data = mission_dict.get("company_data", "")
mission_data.power_math_content = mission_dict.get("power_math_content", "")
mission_data.num_of_user_inputs = mission_dict.get("num_of_user_inputs", 1)
# Convert input labels to Array[String]
var input_labels_array: Array[String] = []
for label in mission_dict.get("input_labels", []):
input_labels_array.append(str(label))
mission_data.input_labels = input_labels_array
# Set companion dialog
mission_data.companion_dialog = mission_dict.get("companion_dialog", {})
converted.append(mission_data)
return converted

File diff suppressed because it is too large Load Diff

@ -1,23 +1,87 @@
extends Resource
class_name MissionObjective
const ObjectiveType = preload("res://configs/data.config.gd").ObjectiveType
const PatternRule = preload("res://scripts/mission/pattern_rule.gd")
const PatternRules = preload("res://scripts/mission/pattern_rules.gd")
@export var type: ObjectiveType
@export var target_count: int = 1
enum ObjectiveType {
BUILD = 0,
POPULATION = 1,
POWER = 2,
PATTERN = 3, # Pattern matching type
LEARNING = 4 # Learning objective type
}
@export var type: ObjectiveType = ObjectiveType.BUILD
@export var target_count: int = 0
@export var current_count: int = 0
@export var description: String = ""
@export var completed: bool = false
@export var structure: Structure # The structure to build/check for
@export var pattern_rules: PatternRules # Rules for pattern matching
@export_subgroup("Structure")
@export var structure: Structure
func check_pattern(builder: Node) -> bool:
if type != ObjectiveType.PATTERN || !pattern_rules:
return false
# Get the grid from the builder
var grid = builder.get_node("Grid")
if !grid:
return false
# Scan the grid for the pattern
var grid_size = grid.get_grid_size()
for x in range(grid_size.x - pattern_rules.pattern_size.x + 1):
for y in range(grid_size.y - pattern_rules.pattern_size.y + 1):
if check_pattern_at_position(grid, Vector2i(x, y)):
return true
return false
func is_completed() -> bool:
return current_count >= target_count
func check_pattern_at_position(grid: Node, start_pos: Vector2i) -> bool:
# Check each position in the pattern
for rule in pattern_rules.rules:
var check_pos = start_pos + rule.offset
var cell = grid.get_cell(check_pos.x, check_pos.y)
# Check if the cell matches the rule
if !cell:
return false
match rule.type:
PatternRule.RuleType.STRUCTURE:
if !cell.has_structure(rule.structure):
return false
PatternRule.RuleType.EMPTY:
if cell.has_any_structure():
return false
PatternRule.RuleType.ROTATION:
if cell.get_rotation() != rule.rotation:
return false
return true
func progress(amount: int = 1) -> void:
current_count = min(current_count + amount, target_count)
completed = is_completed()
func update_progress(builder: Node) -> void:
match type:
ObjectiveType.BUILD:
if structure:
current_count = builder.count_structure(structure)
ObjectiveType.POPULATION:
current_count = builder.get_total_population()
ObjectiveType.POWER:
current_count = builder.get_total_power()
ObjectiveType.PATTERN:
completed = check_pattern(builder)
if completed:
current_count = target_count
ObjectiveType.LEARNING:
# Learning objectives are completed through UI interaction
pass
completed = (type == ObjectiveType.PATTERN && completed) || (type == ObjectiveType.LEARNING && completed) || current_count >= target_count
func is_completed() -> bool:
return completed
# Function to reduce the counter (for demolition)
func regress(amount: int = 1) -> void:

@ -53,12 +53,12 @@ func update_mission_display(mission: MissionData):
label.add_theme_font_size_override("font_size", 16)
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
# Format the objective text
# var progress = ""
# if objective.target_count && objective.type > 1:
# progress = " (%d/%d)" % [objective.current_count, objective.target_count]
# Format the objective text with progress
var progress_text = ""
if objective.target_count > 1:
progress_text = " (%d/%d)" % [objective.current_count, objective.target_count]
label.text = objective.description
label.text = objective.description + progress_text
# Style completed objectives differently
if objective.completed:

@ -0,0 +1,13 @@
extends Resource
class_name PatternRule
enum RuleType {
STRUCTURE, # Check for specific structure
EMPTY, # Check for empty space
ROTATION # Check structure rotation
}
@export var type: RuleType = RuleType.STRUCTURE
@export var offset: Vector2i
@export var structure: Structure # Structure to check for if type is STRUCTURE
@export var rotation: int # Rotation to check for if type is ROTATION

Some files were not shown because too many files have changed in this diff Show More