resolving merge conflicts

pull/18/head
jc-stemuli 2025-04-22 13:00:15 +07:00
parent e6a0cdfbbb
commit 5b35561ce2
85 changed files with 6358 additions and 260 deletions

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

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

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

Binary file not shown.

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,39 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bg21cb3aolpll"
path.s3tc="res://.godot/imported/building-small-a_colormap.png-5c3df3cd6e4a6c3a9b3ecf5882af7d0c.s3tc.ctex"
path.etc2="res://.godot/imported/building-small-a_colormap.png-5c3df3cd6e4a6c3a9b3ecf5882af7d0c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "007d2d2af08d0a09042bcd8910fe8d8f"
}
[deps]
source_file="res://models/building-small-a_colormap.png"
dest_files=["res://.godot/imported/building-small-a_colormap.png-5c3df3cd6e4a6c3a9b3ecf5882af7d0c.s3tc.ctex", "res://.godot/imported/building-small-a_colormap.png-5c3df3cd6e4a6c3a9b3ecf5882af7d0c.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

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dq26m4wm2ukpf"
path="res://.godot/imported/grass-trees-tall_colormap.png-039581d4038a95a52ec1edb7e14a14ad.ctex"
metadata={
"vram_texture": false
}
generator_parameters={
"md5": "007d2d2af08d0a09042bcd8910fe8d8f"
}
[deps]
source_file="res://models/grass-trees-tall_colormap.png"
dest_files=["res://.godot/imported/grass-trees-tall_colormap.png-039581d4038a95a52ec1edb7e14a14ad.ctex"]
[params]
compress/mode=0
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=1

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cucaawfsny5n6"
path="res://.godot/imported/road-straight_colormap.png-b6a117d52548e61c263bb6e65713a481.ctex"
metadata={
"vram_texture": false
}
generator_parameters={
"md5": "007d2d2af08d0a09042bcd8910fe8d8f"
}
[deps]
source_file="res://models/road-straight_colormap.png"
dest_files=["res://.godot/imported/road-straight_colormap.png-b6a117d52548e61c263bb6e65713a481.ctex"]
[params]
compress/mode=0
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=1

@ -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,7 @@ 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"
[display]

@ -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,233 @@
[gd_scene load_steps=6 format=3 uid="uid://b4k3xfm8pd8qw"]
[ext_resource type="Script" uid="uid://bsjmj0qu3xfrr" path="res://scripts/building_selector.gd" id="1_yvr4p"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_button_hover"]
bg_color = Color(0.3, 0.3, 0.3, 0.9)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.5, 0.5, 0.5, 1)
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_button_pressed"]
bg_color = Color(0.4, 0.4, 0.4, 1)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.6, 0.6, 0.6, 1)
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_button"]
bg_color = Color(0.2, 0.2, 0.2, 0.8)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.4, 0.4, 0.4, 1)
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_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="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
script = ExtResource("1_yvr4p")
[node name="MainButton" type="Button" parent="."]
layout_mode = 0
offset_left = 20.0
offset_top = 20.0
offset_right = 170.0
offset_bottom = 60.0
mouse_filter = 1
theme_override_font_sizes/font_size = 16
theme_override_styles/hover = SubResource("StyleBoxFlat_button_hover")
theme_override_styles/pressed = SubResource("StyleBoxFlat_button_pressed")
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
text = "Select Building"
[node name="SelectionPanel" type="Panel" parent="."]
layout_mode = 0
offset_left = 20.0
offset_top = 70.0
offset_right = 380.0
offset_bottom = 620.0
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="SearchBar" type="LineEdit" parent="SelectionPanel"]
layout_mode = 0
offset_left = 10.0
offset_top = 10.0
offset_right = 350.0
offset_bottom = 40.0
placeholder_text = "Search buildings..."
clear_button_enabled = true
[node name="FilterButtons" type="HBoxContainer" parent="SelectionPanel"]
layout_mode = 0
offset_left = 10.0
offset_top = 50.0
offset_right = 350.0
offset_bottom = 90.0
theme_override_constants/separation = 5
[node name="AllButton" type="Button" parent="SelectionPanel/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/hover = SubResource("StyleBoxFlat_button_hover")
theme_override_styles/pressed = SubResource("StyleBoxFlat_button_pressed")
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
toggle_mode = true
button_pressed = true
text = "All"
[node name="ResidentialButton" type="Button" parent="SelectionPanel/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/hover = SubResource("StyleBoxFlat_button_hover")
theme_override_styles/pressed = SubResource("StyleBoxFlat_button_pressed")
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
toggle_mode = true
text = "Residential"
[node name="CommercialButton" type="Button" parent="SelectionPanel/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/hover = SubResource("StyleBoxFlat_button_hover")
theme_override_styles/pressed = SubResource("StyleBoxFlat_button_pressed")
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
toggle_mode = true
text = "Commercial"
[node name="IndustrialButton" type="Button" parent="SelectionPanel/FilterButtons"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/hover = SubResource("StyleBoxFlat_button_hover")
theme_override_styles/pressed = SubResource("StyleBoxFlat_button_pressed")
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
toggle_mode = true
text = "Industrial"
[node name="ScrollContainer" type="ScrollContainer" parent="SelectionPanel"]
layout_mode = 0
offset_left = 10.0
offset_top = 100.0
offset_right = 350.0
offset_bottom = 510.0
horizontal_scroll_mode = 0
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 20
[node name="GroundSection" type="VBoxContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="Label" type="Label" parent="SelectionPanel/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Ground Types"
horizontal_alignment = 1
[node name="GroundOptions" type="GridContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 2
[node name="BuildingSection" type="VBoxContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="Label" type="Label" parent="SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Buildings"
horizontal_alignment = 1
[node name="BuildingOptions" type="GridContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 2
[node name="DescriptionPanel" type="Panel" parent="SelectionPanel"]
layout_mode = 0
offset_left = 10.0
offset_top = 520.0
offset_right = 350.0
offset_bottom = 610.0
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/DescriptionPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 10
[node name="TitleLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "No Building Selected"
horizontal_alignment = 1
[node name="DescriptionLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_font_sizes/font_size = 14
text = "Select a building to view its details"
horizontal_alignment = 1
vertical_alignment = 1
[node name="StatsContainer" type="HBoxContainer" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 20
[node name="PriceLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Price: $0"
[node name="PopulationLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Population: 0"
[node name="PowerLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Power: 0 kW"

@ -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

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

@ -1,8 +1,8 @@
[gd_scene load_steps=64 format=3 uid="uid://vgwrcfy1qawf"]
[gd_scene load_steps=33 format=3 uid="uid://vgwrcfy1qawf"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Script" uid="uid://snp3i7itqnqc" 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="Script" uid="uid://cw0jlq5d8r66g" 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"]
@ -11,9 +11,9 @@
[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="PackedScene" uid="uid://c03crmaif3bss" path="res://scenes/economy_manager.tscn" id="11_2f3dj"]
[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"]
@ -21,6 +21,7 @@
[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://dykbopx8n3c3v" path="res://mission/third_mission.tres" id="17_rrdy6_2"]
[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"]
@ -28,47 +29,25 @@
[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"]
[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!
body_text = "Hi League Community,
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.
Each mission introduces important Math concepts used in urban planning and city management. You'll apply mathematics while watching your city grow.
You are the very first group of students who get to test this. So keep in mind there will be bugs, but do note them.
We are aware of the following bugs:
- Population count may be off by 1
- Lighting Baking in Web Builds are too bright
- We don't restrict building off of roads which will cause workers not to reach buildings
- Building overlap
- No builders for Power Plant
Ready to start planning your city? Click Close to see the controls and begin your first mission!
@ -150,7 +129,7 @@ outro_text_resource = SubResource("Resource_ja86h")
[node name="Builder" type="Node3D" parent="." node_paths=PackedStringArray("selector", "selector_container", "view_camera", "gridmap", "cash_display")]
script = ExtResource("1_jybm7")
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("6_fwsy4"), ExtResource("9_2t3p4"), ExtResource("10_ii8xx"), ExtResource("11_20frt"), ExtResource("12_xtc7p")])
selector = NodePath("Selector")
selector_container = NodePath("Selector/Container")
view_camera = NodePath("../View/Camera")
@ -181,10 +160,7 @@ 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
transform = Transform3D(-0.422618, -0.694272, 0.582563, 0, 0.642788, 0.766044, -0.906308, 0.323744, -0.271654, 0, 0, 0)
shadow_enabled = true
shadow_opacity = 0.75
@ -202,7 +178,7 @@ resource_data = ExtResource("14_76jlq")
[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"), 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("17_rrdy6"), ExtResource("20_ngu16")])
mission_ui = NodePath("MissionPanel")
builder = NodePath("../Builder")
character_scene = ExtResource("18_8lrh8")
@ -240,7 +216,6 @@ 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
@ -389,9 +364,5 @@ text = "Complete"
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,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"]

@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://c6qxpgne58hon"]
[gd_scene load_steps=6 format=3 uid="uid://c6qxpgne58hon"]
[ext_resource type="PackedScene" uid="uid://do24bjaohw3vy" path="res://models/road-straight.glb" id="1_e1p8p"]
[ext_resource type="Script" uid="uid://dm10exi6726ue" path="res://scripts/pathfinding.gd" id="2_3elxe"]

@ -1,4 +1,4 @@
[gd_scene load_steps=3 format=3 uid="uid://b3kdhu8jq41iq"]
[gd_scene load_steps=2 format=3 uid="uid://b3kdhu8jq41iq"]
[ext_resource type="PackedScene" uid="uid://do24bjaohw3vy" path="res://models/road-straight.glb" id="2_kgyfd"]

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

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

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

@ -45,42 +45,16 @@ func _ready():
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)
# Give the construction manager references it needs
construction_manager.builder = self
construction_manager.nav_region = nav_region
# Sound effects now handled in game_manager.gd
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
# Ensure we start with an unlocked structure
# Find first 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)
if structures[i].model != null:
print("Starting with unlocked structure: " + structures[i].model.resource_path)
else:
print("Starting with unlocked structure: (no model)")
break
if not found_unlocked:
@ -133,7 +107,7 @@ func is_mouse_over_ui() -> bool:
# Get HUD dimensions for debug
var hud = get_node_or_null("/root/Main/CanvasLayer/HUD")
if hud:
var hud_rect = hud.get_global_rect()
var _hud_rect = hud.get_global_rect()
# Get HBoxContainer dimensions - this is the actual content area
var hbox = hud.get_node_or_null("HBoxContainer")
@ -173,6 +147,9 @@ func is_mouse_over_ui() -> bool:
func get_mesh(packed_scene):
# Instantiate the scene to access its properties
if packed_scene == null:
return
var scene_instance = packed_scene.instantiate()
var mesh_instance = null
@ -227,13 +204,13 @@ func action_build(gridmap_position):
# For residential buildings, we use the construction manager in mission 3
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")
var is_power_plant = structures[index].model != null and structures[index].model.resource_path.contains("power_plant")
# 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)
# Function to check if we're in mission 3 (when we should use construction workers)
var use_worker_construction = true
var mission_manager = get_node_or_null("/root/Main/MissionManager")
var _mission_manager = get_node_or_null("/root/Main/MissionManager")
# Sound effects are handled via game_manager.gd through the structure_placed signal
if is_road:
@ -254,7 +231,8 @@ func action_build(gridmap_position):
# Make sure any existing NPCs are children of the navigation region
_move_characters_to_navregion()
elif is_power_plant:
#add_power_plant(gridmap_position, index)
# Special handling for power plants - add directly as a child of the builder
_add_power_plant(gridmap_position, index)
# We still set the cell item for collision detection
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
@ -354,7 +332,7 @@ func action_demolish(gridmap_position):
# Check for building model in the scene as a direct child of builder
var building_model_name = "Building_" + str(int(gridmap_position.x)) + "_" + str(int(gridmap_position.z))
var has_building_model = has_node(building_model_name)
var _has_building_model = has_node(building_model_name)
# Store structure index before removal for signaling
var structure_index = -1
@ -422,7 +400,7 @@ func action_demolish(gridmap_position):
# This function is no longer needed since we're using a single NavRegion3D
# Keeping it for compatibility, but it doesn't do anything now
func remove_navigation_region(position: Vector3):
func remove_navigation_region(_pos: Vector3):
# With our new approach using a single nav region, we just rebake
# the entire navigation mesh when roads are added or removed
@ -479,23 +457,48 @@ func update_structure():
for n in selector_container.get_children():
selector_container.remove_child(n)
# Check if we have a valid structure and model
if index < 0 or index >= structures.size():
print("ERROR: Invalid structure index: ", index)
return
var current_structure = structures[index]
if not current_structure:
print("ERROR: Structure at index ", index, " is null")
return
# Create new structure preview in selector
var _model = structures[index].model.instantiate()
selector_container.add_child(_model)
var _model = null
if current_structure.model:
_model = current_structure.model.instantiate()
if not _model:
print("ERROR: Failed to instantiate model for structure at index: ", index)
return
selector_container.add_child(_model)
# Get reference to the selector sprite
var selector_sprite = selector.get_node("Sprite")
if not selector_sprite:
print("ERROR: No Sprite node found in selector")
return
# Apply appropriate scaling based on structure type
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
_model.position = Vector3(-3.0, 0.0, 3.0) # Reset position
else:
# Scale buildings, roads, and decorative terrain to match (3x)
_model.scale = Vector3(3.0, 3.0, 3.0)
_model.position.y += 0.0 # No need for Y adjustment with scaling
if _model:
if current_structure.model.resource_path != null and current_structure.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
_model.position = Vector3(-3.0, 0.0, 3.0) # Reset position
elif (current_structure.type == Structure.StructureType.RESIDENTIAL_BUILDING
or current_structure.type == Structure.StructureType.ROAD
or current_structure.type == Structure.StructureType.TERRAIN
or (current_structure.model.resource_path != null and current_structure.model.resource_path.contains("grass"))):
# Scale buildings, roads, and decorative terrain to match (3x)
_model.scale = Vector3(3.0, 3.0, 3.0)
_model.position.y += 0.0 # No need for Y adjustment with scaling
else:
# Standard positioning for other structures
_model.position.y += 0.25
# Get the selector scale from the structure resource
var scale_factor = structures[index].selector_scale
@ -507,20 +510,30 @@ func update_cash():
cash_display.text = "$" + str(map.cash)
# Function to add a road model as a child of the navigation region
func _add_road_to_navregion(position: Vector3, structure_index: int):
func _add_road_to_navregion(_pos: Vector3, structure_index: int):
# Make sure we have a navigation region
if not nav_region:
setup_navigation_region()
# Create a unique name for this road based on its position
var road_name = "Road_" + str(int(position.x)) + "_" + str(int(position.z))
var road_name = "Road_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# Check if a road with this name already exists
if nav_region.has_node(road_name):
return
# Instantiate the road model using the actual selected structure
var road_model = structures[structure_index].model.instantiate()
# Instantiate the road model - get the actual model based on road type
var road_model
var model_path = structures[structure_index].model.resource_path if structures[structure_index].model != null else ""
if model_path.contains("road-straight"):
# Use the specific road-straight model that works with navmesh
road_model = load("res://models/road-straight.glb").instantiate()
elif model_path.contains("road-corner"):
# Use the specific road-corner model
road_model = load("res://models/road-corner.glb").instantiate()
else:
# Fall back to the structure's model for other road types
road_model = structures[structure_index].model.instantiate()
road_model.name = road_name
@ -528,27 +541,27 @@ func _add_road_to_navregion(position: Vector3, structure_index: int):
nav_region.add_child(road_model)
# Create the transform directly matching the exact one from pathing.tscn
var transform = Transform3D()
var _transform = Transform3D()
# Set scale first
transform.basis = Basis().scaled(Vector3(3.0, 3.0, 3.0))
_transform.basis = Basis().scaled(Vector3(3.0, 3.0, 3.0))
# Then apply rotation from the selector to preserve the rotation the player chose
transform.basis = transform.basis * selector.basis
_transform.basis = _transform.basis * selector.basis
# Set position
transform.origin = position
transform.origin.y = -0.065 # From the pathing scene y offset
_transform.origin = _pos
_transform.origin.y = -0.065 # From the pathing scene y offset
# Apply the complete transform in one go
road_model.transform = transform
road_model.transform = _transform
# Function to add a power plant as a direct child of the builder
func _add_power_plant(position: Vector3, structure_index: int):
func _add_power_plant(_pos: Vector3, structure_index: int):
# Create a unique name for this power plant based on its position
var power_plant_name = "PowerPlant_" + str(int(position.x)) + "_" + str(int(position.z))
var power_plant_name = "PowerPlant_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# Check if a power plant with this name already exists
if has_node(power_plant_name):
@ -562,30 +575,30 @@ func _add_power_plant(position: Vector3, structure_index: int):
add_child(power_plant_model)
# Create the transform
var transform = Transform3D()
var _transform = Transform3D()
# Set scale (using the smaller 0.5x scale)
transform.basis = Basis().scaled(Vector3(0.5, 0.5, 0.5))
_transform.basis = Basis().scaled(Vector3(0.5, 0.5, 0.5))
# Apply rotation from the selector to preserve the rotation the player chose
transform.basis = transform.basis * selector.basis
_transform.basis = _transform.basis * selector.basis
# Set position with offset to center the model at the grid position
transform.origin = position
_transform.origin = _pos
# Apply position offset to center the model (matching the preview)
# These offsets need to be transformed based on the current rotation
var offset = selector.basis * Vector3(0.25, 0, -0.25)
transform.origin += offset
_transform.origin += offset
# Apply the complete transform in one go
power_plant_model.transform = transform
power_plant_model.transform = _transform
# Function to remove a power plant
func _remove_power_plant(position: Vector3):
func _remove_power_plant(_pos: Vector3):
# Get the power plant name based on its position
var power_plant_name = "PowerPlant_" + str(int(position.x)) + "_" + str(int(position.z))
var power_plant_name = "PowerPlant_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# Check if a power plant with this name exists
if has_node(power_plant_name):
@ -598,21 +611,21 @@ func _remove_power_plant(position: Vector3):
pass
# Function to remove a resident model when a residential building is demolished
func _remove_resident_for_building(position: Vector3):
func _remove_resident_for_building(_pos: Vector3):
# First, check if we have a nav region reference
if not nav_region and has_node("NavRegion3D"):
nav_region = get_node("NavRegion3D")
if nav_region:
# Look for resident with matching position in the name
var resident_name = "Resident_" + str(int(position.x)) + "_" + str(int(position.z))
var resident_name = "Resident_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# First try to find by exact name
var found = false
var _found = false
for child in nav_region.get_children():
if child.name.begins_with(resident_name):
child.queue_free()
found = true
_found = true
# Update the HUD population count
var hud = get_node_or_null("/root/Main/CanvasLayer/HUD")
@ -629,14 +642,17 @@ func _update_mission_objective_on_demolish():
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager and mission_manager.current_mission:
# Check if we're in mission 3 (build 40 residential buildings)
if mission_manager.current_mission.id != "3":
# For other missions, use the normal method
var mission_id = mission_manager.current_mission.id
mission_manager.update_objective_progress(mission_id, MissionObjective.ObjectiveType, -1)
mission_manager.update_objective_progress(mission_id, MissionObjective.ObjectiveType.BUILD_RESIDENTIAL, -1)
# Function to remove terrain (grass or trees)
func _remove_terrain(position: Vector3):
func _remove_terrain(_pos: Vector3):
# Get the terrain name based on its position
var terrain_name = "Terrain_" + str(int(position.x)) + "_" + str(int(position.z))
var terrain_name = "Terrain_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
var grass_name = "Grass_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# Check if terrain with this name exists
if has_node(terrain_name):
@ -648,15 +664,15 @@ func _remove_terrain(position: Vector3):
pass
# Function to remove building model from scene
func _remove_building_model(position: Vector3):
func _remove_building_model(_pos: Vector3):
# Try multiple possible naming patterns
var building_patterns = [
"Building_" + str(int(position.x)) + "_" + str(int(position.z)),
"building-small-a_" + str(int(position.x)) + "_" + str(int(position.z)),
"building-small-b_" + str(int(position.x)) + "_" + str(int(position.z)),
"building-small-c_" + str(int(position.x)) + "_" + str(int(position.z)),
"building-small-d_" + str(int(position.x)) + "_" + str(int(position.z)),
"building-garage_" + str(int(position.x)) + "_" + str(int(position.z))
"Building_" + str(int(_pos.x)) + "_" + str(int(_pos.z)),
"building-small-a_" + str(int(_pos.x)) + "_" + str(int(_pos.z)),
"building-small-b_" + str(int(_pos.x)) + "_" + str(int(_pos.z)),
"building-small-c_" + str(int(_pos.x)) + "_" + str(int(_pos.z)),
"building-small-d_" + str(int(_pos.x)) + "_" + str(int(_pos.z)),
"building-garage_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
]
# Check if we can find the building model with any of the pattern names
@ -677,7 +693,7 @@ func _remove_building_model(position: Vector3):
continue
# Check if this building is at our position (with some tolerance)
var pos_diff = (child.global_transform.origin - position).abs()
var pos_diff = (child.global_transform.origin - _pos).abs()
if pos_diff.x < 0.5 and pos_diff.z < 0.5:
child.queue_free()
found = true
@ -693,7 +709,7 @@ func _remove_building_model(position: Vector3):
continue
# Check if this building is at our position (with some tolerance)
var pos_diff = (child.global_transform.origin - position).abs()
var pos_diff = (child.global_transform.origin - _pos).abs()
if pos_diff.x < 0.5 and pos_diff.z < 0.5:
child.queue_free()
found = true
@ -703,20 +719,20 @@ func _remove_building_model(position: Vector3):
if !found and gridmap:
for child in gridmap.get_children():
# Check if this is any model at our position (with some tolerance)
var pos_diff = (child.global_transform.origin - position).abs()
var pos_diff = (child.global_transform.origin - _pos).abs()
if pos_diff.x < 0.5 and pos_diff.z < 0.5:
child.queue_free()
found = true
break
# Function to remove a road model from the navigation region
func _remove_road_from_navregion(position: Vector3):
func _remove_road_from_navregion(_pos: Vector3):
# Make sure we have a navigation region
if not nav_region:
return
# Get the road name based on its position
var road_name = "Road_" + str(int(position.x)) + "_" + str(int(position.z))
var road_name = "Road_" + str(int(_pos.x)) + "_" + str(int(_pos.z))
# Check if a road with this name exists
if nav_region.has_node(road_name):
@ -739,7 +755,7 @@ func _add_existing_roads_to_navregion():
child.queue_free()
# Find all road cells in the gridmap
var added_count = 0
var _added_count = 0
# We need to convert any existing roads in the GridMap to our new system
# Find existing road cells and add them to the NavRegion3D, then clear from GridMap
@ -751,7 +767,7 @@ func _add_existing_roads_to_navregion():
_add_road_to_navregion(cell, structure_index)
# Remove from the GridMap since we're now handling roads differently
gridmap.set_cell_item(cell, -1)
added_count += 1
_added_count += 1
# Function to move all character NPCs to be children of the navigation region
func _move_characters_to_navregion():
@ -779,39 +795,8 @@ func _move_characters_to_navregion():
character.global_transform.origin = global_pos
# Function to add terrain (grass or trees) as a direct child
func _add_terrain(position: Vector3, structure_index: int):
# Create a unique name for this terrain element based on its position
var terrain_name = "Terrain_" + str(int(position.x)) + "_" + str(int(position.z))
# Check if terrain with this name already exists
if has_node(terrain_name):
return
# Instantiate the terrain model
var terrain_model = structures[structure_index].model.instantiate()
terrain_model.name = terrain_name
# Add the terrain model directly to the builder (this node)
add_child(terrain_model)
# Create the transform
var transform = Transform3D()
# Set scale (using 3.0 scale as per other terrain elements)
transform.basis = Basis().scaled(Vector3(3.0, 3.0, 3.0))
# Apply rotation from the selector to preserve the rotation the player chose
transform.basis = transform.basis * selector.basis
# Set position
transform.origin = position
# Apply the complete transform in one go
terrain_model.transform = transform
# Callback for when construction is completed
func _on_construction_completed(position: Vector3):
func _on_construction_completed(_pos: Vector3):
# We need to find a residential structure index to add to gridmap
var residential_index = -1
for i in range(structures.size()):
@ -824,14 +809,14 @@ func _on_construction_completed(position: Vector3):
var rotation_index = 0
# Try to get the rotation index from the construction manager
if construction_manager and construction_manager.construction_sites.has(position):
var site = construction_manager.construction_sites[position]
if construction_manager and construction_manager.construction_sites.has(_pos):
var site = construction_manager.construction_sites[_pos]
if site.has("rotation_index"):
rotation_index = site["rotation_index"]
# Add the completed residential building to the gridmap with the correct rotation
gridmap.set_cell_item(position, residential_index, rotation_index)
gridmap.set_cell_item(_pos, residential_index, rotation_index)
# Check if we need to spawn a character for mission 1
var mission_manager = get_node_or_null("/root/Main/MissionManager")
@ -843,7 +828,7 @@ func _on_construction_completed(position: Vector3):
# 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)
mission_manager._spawn_character_on_road(_pos)
# NOTE: We removed the structure_placed signal emission here to fix the population double-counting
else:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,279 @@
extends Control
@onready var main_button = $MainButton
@onready var selection_panel = $SelectionPanel
@onready var ground_options = $SelectionPanel/ScrollContainer/VBoxContainer/GroundSection/GroundOptions
@onready var building_options = $SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection/BuildingOptions
@onready var search_bar = $SelectionPanel/SearchBar
@onready var filter_buttons = $SelectionPanel/FilterButtons
@onready var description_panel = $SelectionPanel/DescriptionPanel
@onready var title_label = $SelectionPanel/DescriptionPanel/VBoxContainer/TitleLabel
@onready var description_label = $SelectionPanel/DescriptionPanel/VBoxContainer/DescriptionLabel
@onready var price_label = $SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer/PriceLabel
@onready var population_label = $SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer/PopulationLabel
@onready var power_label = $SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer/PowerLabel
@export var builder: Node:
set(value):
_builder = value
if is_inside_tree(): # Only create buttons if node is ready
_create_option_buttons()
get:
return _builder
var _builder: Node
var current_selection: int = 0
var is_panel_visible: bool = false
var current_filter: String = "All"
var search_text: String = ""
func _ready():
# Connect the main button signal
main_button.pressed.connect(_on_main_button_pressed)
# Connect search bar signal
search_bar.text_changed.connect(_on_search_text_changed)
# Connect filter button signals
for button in filter_buttons.get_children():
if button is Button:
button.pressed.connect(_on_filter_button_pressed.bind(button.text))
# Initially hide the selection panel
selection_panel.visible = false
# Make sure the panel doesn't pass through mouse events
selection_panel.mouse_filter = Control.MOUSE_FILTER_STOP
ground_options.mouse_filter = Control.MOUSE_FILTER_STOP
building_options.mouse_filter = Control.MOUSE_FILTER_STOP
# Create the building and ground option buttons
_create_option_buttons()
func _create_option_buttons():
# Clear existing buttons
for child in ground_options.get_children():
child.queue_free()
for child in building_options.get_children():
child.queue_free()
# Get structures from builder
if not _builder or not _builder.structures:
return
# Create ground options (grass, pavement, etc.)
var ground_structures = []
var building_structures = []
# Sort structures by type and apply filters
for structure in _builder.structures:
# 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:
"Residential":
if structure.type != Structure.StructureType.RESIDENTIAL_BUILDING:
continue
"Commercial":
if structure.type != Structure.StructureType.COMMERCIAL_BUILDING:
continue
"Industrial":
if structure.type != Structure.StructureType.INDUSTRIAL_BUILDING:
continue
if structure.type == Structure.StructureType.TERRAIN:
ground_structures.append(structure)
else:
building_structures.append(structure)
# Set up grid layout for ground options
ground_options.columns = 4 # Set number of columns
ground_options.add_theme_constant_override("h_separation", 10) # Horizontal spacing
ground_options.add_theme_constant_override("v_separation", 10) # Vertical spacing
# Create buttons for ground structures
for i in range(ground_structures.size()):
var button = _create_option_button(ground_structures[i], i)
ground_options.add_child(button)
# Set up grid layout for building options
building_options.columns = 4 # Set number of columns
building_options.add_theme_constant_override("h_separation", 10) # Horizontal spacing
building_options.add_theme_constant_override("v_separation", 10) # Vertical spacing
# Create buttons for building structures
for i in range(building_structures.size()):
var button = _create_option_button(building_structures[i], i + ground_structures.size())
building_options.add_child(button)
# Hide sections if they have no options
$SelectionPanel/ScrollContainer/VBoxContainer/GroundSection.visible = ground_structures.size() > 0
$SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection.visible = building_structures.size() > 0
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_main_button_pressed():
is_panel_visible = !is_panel_visible
selection_panel.visible = is_panel_visible
if is_panel_visible:
# Update button states to show current selection
_update_button_states()
func _on_search_text_changed(new_text: String):
search_text = new_text
_create_option_buttons()
func _on_filter_button_pressed(filter_name: String):
# Update filter buttons
for button in filter_buttons.get_children():
if button is Button:
button.button_pressed = (button.text == filter_name)
current_filter = filter_name
_create_option_buttons()
func _on_option_selected(index: int):
if not _builder:
print("ERROR: No builder reference in building selector")
return
if not _builder.structures or index < 0 or index >= _builder.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
var all_buttons = ground_options.get_children() + building_options.get_children()
for i in range(all_buttons.size()):
all_buttons[i].button_pressed = (i == current_selection)
func _get_structure_name(index: int) -> String:
if _builder and _builder.structures and index >= 0 and index < _builder.structures.size():
var structure = _builder.structures[index]
return structure.title
return "Unknown"
func _update_description_panel(index: int):
if not _builder or not _builder.structures or index < 0 or index >= _builder.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 = _builder.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

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

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

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

@ -0,0 +1 @@
uid://3r6pls585y3h

@ -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

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

@ -76,6 +76,32 @@ func _ready():
# Make sure sound buses are properly configured
call_deferred("_setup_sound_buses")
# Connect the building selector to the builder
var building_selector = $CanvasLayer/BuildingSelector
if building_selector:
building_selector.builder = $Builder
# 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 HUD with current values
hud_manager.update_money(economy_manager.money)
hud_manager.update_population_count(economy_manager.population)
hud_manager.update_energy_balance(economy_manager.energy_production, economy_manager.energy_consumption)
# Initialize all game audio - called immediately on desktop, after user interaction on web
func _initialize_game_audio():

@ -0,0 +1 @@
uid://7upjj8h5aqo6

@ -1,15 +1,16 @@
extends Node
# Signals
signal population_updated(new_population)
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
# References
var buildeJuj
var builder
var building_construction_manager
var population_label: Label
var electricity_label: Label
@ -18,17 +19,15 @@ var population_tooltip: Control
var electricity_tooltip: Control
var controls_panel: PanelContainer
var sound_panel: PanelContainer
var builder:Node
var cash_label: Label
func _ready():
# Connect to signals from the builder
builder = get_node_or_null("/root/Main/Builder")
population_updated.connect(update_population_count)
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
@ -36,8 +35,14 @@ func _ready():
electricity_indicator = $HBoxContainer/ElectricityItem/ElectricityValues/ElectricityIndicator
population_tooltip = $PopulationTooltip
electricity_tooltip = $ElectricityTooltip
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
# Ensure electricity indicator starts with red color
if electricity_indicator:
@ -62,15 +67,7 @@ func _ready():
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)
update_hud()
# Called when a structure is placed
func _on_structure_placed(structure_index, position):
@ -131,7 +128,7 @@ func _on_structure_removed(structure_index, position):
# Update Population
func set_population_count(count: int):
func update_population_count(count: int):
total_population += count
population_label.text = str(total_population)

@ -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()

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

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

@ -20,3 +20,4 @@ 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

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

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

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

@ -11,8 +11,23 @@ var react_sound_bridge = null # Will be instantiated from a script
var audio_bridge = null # Will be instantiated from a script
# Volume ranges from 0.0 to 1.0
var music_volume: float = 0.0
var sfx_volume: float = 0.1
var music_volume: float = 0.8:
set(value):
music_volume = value
_apply_music_volume()
_save_settings()
music_volume_changed.emit(value)
get:
return music_volume
var sfx_volume: float = 0.8:
set(value):
sfx_volume = value
_apply_sfx_volume()
_save_settings()
sfx_volume_changed.emit(value)
get:
return sfx_volume
# Mute states
var music_muted: bool = false
@ -189,10 +204,10 @@ func _on_react_audio_ready():
audio_ready.emit()
# Called when AudioBridge connects to the platform-one sound manager
func _on_audio_bridge_connected(is_connected: bool):
print("AudioBridge connected: ", is_connected)
func _on_audio_bridge_connected(connected: bool):
print("AudioBridge connected: ", connected)
if is_connected:
if connected:
audio_initialized = true
# Request the sound state from the platform-one sound manager
@ -221,24 +236,42 @@ func init_web_audio_from_js():
#if OS.has_feature("web") and not audio_initialized:
#_initialize_web_audio()
# Initialize audio for web builds
# Initialize web audio system
func _initialize_web_audio():
# Only initialize if we haven't already
if audio_initialized:
return
# For web builds, we notify JavaScript to initialize audio
# if OS.has_feature("web"):
# JSBridge.JavaScriptGlobal.handle_audio_action("INITIALIZE_AUDIO")
#
# # We don't need to create any dummy players, as JavaScript will handle the audio
# audio_initialized = true
# audio_ready.emit()
# return
# For non-web platforms, initialize Godot audio (this shouldn't get called)
# if not OS.has_feature("web"):
# Set the flag to prevent multiple initializations
# Try to initialize the audio context
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval("""
(function() {
try {
// Create or get the audio context
if (!window._godotAudioContext) {
window._godotAudioContext = new (window.AudioContext || window.webkitAudioContext)();
console.log('SoundManager: Created new audio context');
}
// Resume the audio context if it's suspended
if (window._godotAudioContext.state === 'suspended') {
window._godotAudioContext.resume();
console.log('SoundManager: Resumed audio context');
}
// Set initialized flag
window._godotAudioInitialized = true;
} catch(e) {
console.error('SoundManager: Error initializing audio:', e);
}
})()
""")
# Set our initialized flag
audio_initialized = true
# Emit the ready signal
audio_ready.emit()
# Play background music
@ -252,7 +285,7 @@ func play_music(sound_name: String, loop: bool = true):
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# if audio_bridge != null and audio_bridge.get("connected") == true:
# print("Using AudioBridge to play music: ", sound_name)
# if audio_bridge.has_method("play_music") and audio_bridge.play_music(sound_name):
# return
@ -300,7 +333,7 @@ func play_sfx(sound_name: String):
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#if audio_bridge != null and audio_bridge.get("connected") == true:
#print("Using AudioBridge to play sfx: ", sound_name)
#if audio_bridge.has_method("play_sfx") and audio_bridge.play_sfx(sound_name):
#return
@ -350,7 +383,7 @@ func stop_music():
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#if audio_bridge != null and audio_bridge.get("connected") == true:
#print("Using AudioBridge to stop music")
#if audio_bridge.has_method("stop_music") and audio_bridge.stop_music():
#current_music = ""
@ -377,7 +410,7 @@ func set_music_volume(volume: float):
pass
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# if audio_bridge != null and audio_bridge.get("connected") == true:
# print("Using AudioBridge to set music volume: ", music_volume)
# if audio_bridge.has_method("set_music_volume"):
# audio_bridge.set_music_volume(music_volume)
@ -401,7 +434,7 @@ func set_sfx_volume(volume: float):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# if audio_bridge != null and audio_bridge.get("connected") == true:
# print("Using AudioBridge to set sfx volume: ", sfx_volume)
# if audio_bridge.has_method("set_sfx_volume"):
# audio_bridge.set_sfx_volume(sfx_volume)
@ -425,7 +458,7 @@ func toggle_music_mute():
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# if audio_bridge != null and audio_bridge.get("connected") == true:
# print("Using AudioBridge to toggle music mute: ", music_muted)
# if audio_bridge.has_method("toggle_music_mute"):
# audio_bridge.toggle_music_mute()
@ -449,7 +482,7 @@ func toggle_sfx_mute():
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# if audio_bridge != null and audio_bridge.get("connected") == true:
# print("Using AudioBridge to toggle sfx mute: ", sfx_muted)
# if audio_bridge.has_method("toggle_sfx_mute"):
# audio_bridge.toggle_sfx_mute()

@ -0,0 +1,557 @@
extends Node
signal music_volume_changed(new_volume)
signal sfx_volume_changed(new_volume)
signal music_muted_changed(is_muted)
signal sfx_muted_changed(is_muted)
signal audio_ready # Signal emitted when audio is initialized (important for web)
# Sound bridges for web builds
var react_sound_bridge = null # Will be instantiated from a script
var audio_bridge = null # Will be instantiated from a script
# Volume ranges from 0.0 to 1.0
<<<<<<< Updated upstream
var music_volume: float = 0.0
var sfx_volume: float = 0.1
=======
var music_volume: float = 0.8:
set(value):
music_volume = value
_apply_music_volume()
_save_settings()
music_volume_changed.emit(value)
get:
return music_volume
var sfx_volume: float = 0.8:
set(value):
sfx_volume = value
_apply_sfx_volume()
_save_settings()
sfx_volume_changed.emit(value)
get:
return sfx_volume
>>>>>>> Stashed changes
# Mute states
var music_muted: bool = false:
set(value):
music_muted = value
_apply_music_volume()
_save_settings()
music_muted_changed.emit(value)
get:
return music_muted
var sfx_muted: bool = false:
set(value):
sfx_muted = value
_apply_sfx_volume()
_save_settings()
sfx_muted_changed.emit(value)
get:
return sfx_muted
# Bus indices for easier reference
var music_bus_index: int
var sfx_bus_index: int
# Default bus names
const MUSIC_BUS_NAME = "Music"
const SFX_BUS_NAME = "SFX"
var audio_initialized: bool = false
# Sound files dictionary - mapping simplified names to file paths
const SOUND_FILES = {
"jazzNewOrleans": "res://sounds/jazz_new_orleans.mp3",
"lofiChillJazz": "res://sounds/lofi-chill-jazz-272869.mp3",
"buildingPlacing": "res://sounds/building_placing.wav",
"construction": "res://sounds/construction.wav",
"powerDrill": "res://sounds/power_drill.mp3"
}
# Current music track
var current_music: String = ""
# Currently playing audio streams (for direct Godot playback)
var music_player: AudioStreamPlayer = null
var sfx_players: Dictionary = {}
const MIN_VOLUME = -40
const MAX_VOLUME = 0
const CONFIG_PATH = "user://audio_settings.cfg"
func _ready():
print("SoundManager: Initializing...")
# Create music player for all platforms
music_player = AudioStreamPlayer.new()
add_child(music_player)
# Set up the audio buses first
_setup_audio_buses()
# Load and apply settings
_load_settings()
# Set the music player bus
music_player.bus = MUSIC_BUS_NAME
# Initialize audio
audio_initialized = true
print("SoundManager: Initialization complete")
print("SoundManager: Music Volume: ", music_volume, " Muted: ", music_muted)
print("SoundManager: SFX Volume: ", sfx_volume, " Muted: ", sfx_muted)
# Emit the audio ready signal
audio_ready.emit()
# Setup audio buses (doesn't start audio playback)
func _setup_audio_buses():
# Initialize audio bus indices
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
# If the buses don't exist yet, create them
if music_bus_index == -1:
# Create music bus
music_bus_index = AudioServer.bus_count
AudioServer.add_bus()
AudioServer.set_bus_name(music_bus_index, MUSIC_BUS_NAME)
AudioServer.set_bus_send(music_bus_index, "Master")
if sfx_bus_index == -1:
# Create SFX bus
sfx_bus_index = AudioServer.bus_count
AudioServer.add_bus()
AudioServer.set_bus_name(sfx_bus_index, SFX_BUS_NAME)
AudioServer.set_bus_send(sfx_bus_index, "Master")
# Verify buses were created correctly
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
# Apply initial settings
_apply_music_volume()
_apply_sfx_volume()
# Make sure buses aren't muted by default
if music_bus_index != -1:
AudioServer.set_bus_mute(music_bus_index, false)
if sfx_bus_index != -1:
AudioServer.set_bus_mute(sfx_bus_index, false)
# Process sound state received from JavaScript
func process_js_audio_state(state: Dictionary):
# Update local state based on received data
if state.has("musicVolume"):
music_volume = state.musicVolume
if state.has("sfxVolume"):
sfx_volume = state.sfxVolume
if state.has("musicMuted"):
music_muted = state.musicMuted
if state.has("sfxMuted"):
sfx_muted = state.sfxMuted
if state.has("currentMusic"):
current_music = state.currentMusic
# Emit signals about changes
music_volume_changed.emit(music_volume)
sfx_volume_changed.emit(sfx_volume)
music_muted_changed.emit(music_muted)
sfx_muted_changed.emit(sfx_muted)
# Called when ReactSoundBridge reports it's ready
func _on_react_audio_ready():
print("ReactSoundBridge reports ready")
audio_initialized = true
# Update local state from React
if react_sound_bridge != null:
if react_sound_bridge.get("music_volume") != null:
music_volume = react_sound_bridge.music_volume
if react_sound_bridge.get("sfx_volume") != null:
sfx_volume = react_sound_bridge.sfx_volume
if react_sound_bridge.get("music_muted") != null:
music_muted = react_sound_bridge.music_muted
if react_sound_bridge.get("sfx_muted") != null:
sfx_muted = react_sound_bridge.sfx_muted
if react_sound_bridge.get("current_music") != null:
current_music = react_sound_bridge.current_music
# Emit the audio ready signal
audio_ready.emit()
# Called when AudioBridge connects to the platform-one sound manager
func _on_audio_bridge_connected(is_connected: bool):
print("AudioBridge connected: ", is_connected)
if is_connected:
audio_initialized = true
# Request the sound state from the platform-one sound manager
if audio_bridge.has_method("get_sound_state"):
audio_bridge.get_sound_state()
# Emit the audio ready signal
audio_ready.emit()
# Called when any user interaction happens in web builds
func _on_user_interaction(_arg=null):
if OS.has_feature("web") and not audio_initialized:
_initialize_web_audio()
# Process input events directly
func _input(event):
if OS.has_feature("web") and not audio_initialized:
if event is InputEventMouseButton or event is InputEventKey:
if event.pressed:
_initialize_web_audio()
# If this method is called from JavaScript, it will help the game to
# initialize audio properly in web builds
func init_web_audio_from_js():
pass
#if OS.has_feature("web") and not audio_initialized:
#_initialize_web_audio()
# Initialize audio for web builds
func _initialize_web_audio():
if audio_initialized:
return
# For web builds, we notify JavaScript to initialize audio
# if OS.has_feature("web"):
# JSBridge.JavaScriptGlobal.handle_audio_action("INITIALIZE_AUDIO")
#
# # We don't need to create any dummy players, as JavaScript will handle the audio
# audio_initialized = true
# audio_ready.emit()
# return
# For non-web platforms, initialize Godot audio (this shouldn't get called)
# if not OS.has_feature("web"):
# Set the flag to prevent multiple initializations
audio_initialized = true
audio_ready.emit()
# Play background music
func play_music(sound_name: String, loop: bool = true):
if not audio_initialized:
return
# Store the current music name
current_music = sound_name
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to play music: ", sound_name)
# if audio_bridge.has_method("play_music") and audio_bridge.play_music(sound_name):
# return
#
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to play music: ", sound_name)
# JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_MUSIC", sound_name)
# return
# For native builds, use Godot audio
if not SOUND_FILES.has(sound_name):
return
# Get the file path
var file_path = SOUND_FILES[sound_name]
# Load the audio stream
var stream = load(file_path)
if stream == null:
return
# Stop current music if playing
if music_player.playing:
music_player.stop()
# Set up and play the music
music_player.stream = stream
music_player.bus = MUSIC_BUS_NAME
# Set looping if supported by the stream
if stream is AudioStreamMP3 or stream is AudioStreamOggVorbis:
stream.loop = loop
# Ensure volume is set correctly before playing
_apply_music_volume()
# Play the music
music_player.play()
# Play a sound effect
func play_sfx(sound_name: String):
if not audio_initialized:
return
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#print("Using AudioBridge to play sfx: ", sound_name)
#if audio_bridge.has_method("play_sfx") and audio_bridge.play_sfx(sound_name):
#return
#
## Fall back to JavaScript Bridge
#print("Using JavaScriptBridge to play sfx: ", sound_name)
#JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_SFX", sound_name)
#return
# For native builds, use Godot audio
if not SOUND_FILES.has(sound_name):
return
# Get the file path
var file_path = SOUND_FILES[sound_name]
# Load the audio stream
var stream = load(file_path)
if stream == null:
return
# Create or reuse a player for this sound
var player: AudioStreamPlayer
if not sfx_players.has(sound_name):
player = AudioStreamPlayer.new()
add_child(player)
sfx_players[sound_name] = player
else:
player = sfx_players[sound_name]
if player.playing:
player.stop()
# Set up and play the sound
player.stream = stream
if sfx_muted:
player.volume_db = linear_to_db(0)
else:
player.volume_db = linear_to_db(sfx_volume)
player.bus = SFX_BUS_NAME
player.play()
# Stop background music
func stop_music():
if not audio_initialized:
return
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#print("Using AudioBridge to stop music")
#if audio_bridge.has_method("stop_music") and audio_bridge.stop_music():
#current_music = ""
#return
#
## Fall back to JavaScript Bridge
#print("Using JavaScriptBridge to stop music")
#JSBridge.JavaScriptGlobal.handle_audio_action("STOP_MUSIC")
#current_music = ""
#return
# For native builds, use Godot audio
if music_player and music_player.playing:
music_player.stop()
current_music = ""
# Set music volume (0.0 to 1.0)
func set_music_volume(volume: float):
music_volume = clampf(volume, 0.0, 1.0)
# For web builds, try multiple bridge options
if false:
pass
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to set music volume: ", music_volume)
# if audio_bridge.has_method("set_music_volume"):
# audio_bridge.set_music_volume(music_volume)
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to set music volume: ", music_volume)
# JSBridge.JavaScriptGlobal.handle_audio_action("SET_MUSIC_VOLUME", "", music_volume)
else:
# Apply to local Godot audio system
_apply_music_volume()
# Emit signal
music_volume_changed.emit(music_volume)
# Set SFX volume (0.0 to 1.0)
func set_sfx_volume(volume: float):
sfx_volume = clampf(volume, 0.0, 1.0)
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to set sfx volume: ", sfx_volume)
# if audio_bridge.has_method("set_sfx_volume"):
# audio_bridge.set_sfx_volume(sfx_volume)
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to set sfx volume: ", sfx_volume)
# JSBridge.JavaScriptGlobal.handle_audio_action("SET_SFX_VOLUME", "", sfx_volume)
else:
# Apply to local Godot audio system
_apply_sfx_volume()
# Emit signal
sfx_volume_changed.emit(sfx_volume)
# Toggle music mute state
func toggle_music_mute():
music_muted = !music_muted
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to toggle music mute: ", music_muted)
# if audio_bridge.has_method("toggle_music_mute"):
# audio_bridge.toggle_music_mute()
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to toggle music mute: ", music_muted)
# JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_MUSIC_MUTE")
else:
# Apply to local Godot audio system
_apply_music_volume()
# Emit signal
music_muted_changed.emit(music_muted)
# Toggle SFX mute state
func toggle_sfx_mute():
sfx_muted = !sfx_muted
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to toggle sfx mute: ", sfx_muted)
# if audio_bridge.has_method("toggle_sfx_mute"):
# audio_bridge.toggle_sfx_mute()
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to toggle sfx mute: ", sfx_muted)
# JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_SFX_MUTE")
else:
# Apply to local Godot audio system
_apply_sfx_volume()
# Emit signal
sfx_muted_changed.emit(sfx_muted)
# Apply music volume settings
func _apply_music_volume():
if not audio_initialized:
return
var db = linear_to_db(music_volume)
print("SoundManager: Applying music volume: ", music_volume, " (", db, "dB)")
if music_bus_index != -1:
AudioServer.set_bus_mute(music_bus_index, music_muted)
if not music_muted:
AudioServer.set_bus_volume_db(music_bus_index, db)
if music_player != null:
music_player.volume_db = -80.0 if music_muted else db
# Apply SFX volume settings
func _apply_sfx_volume():
if not audio_initialized:
return
var db = linear_to_db(sfx_volume)
print("SoundManager: Applying SFX volume: ", sfx_volume, " (", db, "dB)")
if sfx_bus_index != -1:
AudioServer.set_bus_mute(sfx_bus_index, sfx_muted)
if not sfx_muted:
AudioServer.set_bus_volume_db(sfx_bus_index, db)
for player in sfx_players.values():
if player != null:
player.volume_db = -80.0 if sfx_muted else db
# Helper function to convert linear volume to decibels
func linear_to_db(linear_value: float) -> float:
if linear_value <= 0:
return -80.0
return 20.0 * log(linear_value) / log(10.0)
func _load_settings():
print("SoundManager: Loading settings from ", CONFIG_PATH)
var config = ConfigFile.new()
var err = config.load(CONFIG_PATH)
if err == OK:
print("SoundManager: Successfully loaded settings file")
# Load saved values
music_volume = config.get_value("audio", "music_volume", 0.8)
sfx_volume = config.get_value("audio", "sfx_volume", 0.8)
music_muted = config.get_value("audio", "music_muted", false)
sfx_muted = config.get_value("audio", "sfx_muted", false)
else:
print("SoundManager: No settings file found, using defaults")
# Set default values
music_volume = 0.8
sfx_volume = 0.8
music_muted = false
sfx_muted = false
# Save the default values
_save_settings()
print("SoundManager: Loaded values - Music: ", music_volume, " SFX: ", sfx_volume)
# Apply the loaded settings immediately
_apply_music_volume()
_apply_sfx_volume()
func _save_settings():
print("SoundManager: Saving settings...")
var config = ConfigFile.new()
# Save current values
config.set_value("audio", "music_volume", music_volume)
config.set_value("audio", "sfx_volume", sfx_volume)
config.set_value("audio", "music_muted", music_muted)
config.set_value("audio", "sfx_muted", sfx_muted)
# Save to file
var err = config.save(CONFIG_PATH)
if err == OK:
print("SoundManager: Settings saved successfully")
else:
print("SoundManager: Error saving settings: ", err)
func get_music_volume() -> float:
if music_muted:
return MIN_VOLUME
return music_volume
func get_sfx_volume() -> float:
if sfx_muted:
return MIN_VOLUME
return sfx_volume

@ -0,0 +1,557 @@
extends Node
signal music_volume_changed(new_volume)
signal sfx_volume_changed(new_volume)
signal music_muted_changed(is_muted)
signal sfx_muted_changed(is_muted)
signal audio_ready # Signal emitted when audio is initialized (important for web)
# Sound bridges for web builds
var react_sound_bridge = null # Will be instantiated from a script
var audio_bridge = null # Will be instantiated from a script
# Volume ranges from 0.0 to 1.0
<<<<<<< Updated upstream
var music_volume: float = 0.0
var sfx_volume: float = 0.1
=======
var music_volume: float = 0.8:
set(value):
music_volume = value
_apply_music_volume()
_save_settings()
music_volume_changed.emit(value)
get:
return music_volume
var sfx_volume: float = 0.8:
set(value):
sfx_volume = value
_apply_sfx_volume()
_save_settings()
sfx_volume_changed.emit(value)
get:
return sfx_volume
>>>>>>> Stashed changes
# Mute states
var music_muted: bool = false:
set(value):
music_muted = value
_apply_music_volume()
_save_settings()
music_muted_changed.emit(value)
get:
return music_muted
var sfx_muted: bool = false:
set(value):
sfx_muted = value
_apply_sfx_volume()
_save_settings()
sfx_muted_changed.emit(value)
get:
return sfx_muted
# Bus indices for easier reference
var music_bus_index: int
var sfx_bus_index: int
# Default bus names
const MUSIC_BUS_NAME = "Music"
const SFX_BUS_NAME = "SFX"
var audio_initialized: bool = false
# Sound files dictionary - mapping simplified names to file paths
const SOUND_FILES = {
"jazzNewOrleans": "res://sounds/jazz_new_orleans.mp3",
"lofiChillJazz": "res://sounds/lofi-chill-jazz-272869.mp3",
"buildingPlacing": "res://sounds/building_placing.wav",
"construction": "res://sounds/construction.wav",
"powerDrill": "res://sounds/power_drill.mp3"
}
# Current music track
var current_music: String = ""
# Currently playing audio streams (for direct Godot playback)
var music_player: AudioStreamPlayer = null
var sfx_players: Dictionary = {}
const MIN_VOLUME = -40
const MAX_VOLUME = 0
const CONFIG_PATH = "user://audio_settings.cfg"
func _ready():
print("SoundManager: Initializing...")
# Create music player for all platforms
music_player = AudioStreamPlayer.new()
add_child(music_player)
# Set up the audio buses first
_setup_audio_buses()
# Load and apply settings
_load_settings()
# Set the music player bus
music_player.bus = MUSIC_BUS_NAME
# Initialize audio
audio_initialized = true
print("SoundManager: Initialization complete")
print("SoundManager: Music Volume: ", music_volume, " Muted: ", music_muted)
print("SoundManager: SFX Volume: ", sfx_volume, " Muted: ", sfx_muted)
# Emit the audio ready signal
audio_ready.emit()
# Setup audio buses (doesn't start audio playback)
func _setup_audio_buses():
# Initialize audio bus indices
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
# If the buses don't exist yet, create them
if music_bus_index == -1:
# Create music bus
music_bus_index = AudioServer.bus_count
AudioServer.add_bus()
AudioServer.set_bus_name(music_bus_index, MUSIC_BUS_NAME)
AudioServer.set_bus_send(music_bus_index, "Master")
if sfx_bus_index == -1:
# Create SFX bus
sfx_bus_index = AudioServer.bus_count
AudioServer.add_bus()
AudioServer.set_bus_name(sfx_bus_index, SFX_BUS_NAME)
AudioServer.set_bus_send(sfx_bus_index, "Master")
# Verify buses were created correctly
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
# Apply initial settings
_apply_music_volume()
_apply_sfx_volume()
# Make sure buses aren't muted by default
if music_bus_index != -1:
AudioServer.set_bus_mute(music_bus_index, false)
if sfx_bus_index != -1:
AudioServer.set_bus_mute(sfx_bus_index, false)
# Process sound state received from JavaScript
func process_js_audio_state(state: Dictionary):
# Update local state based on received data
if state.has("musicVolume"):
music_volume = state.musicVolume
if state.has("sfxVolume"):
sfx_volume = state.sfxVolume
if state.has("musicMuted"):
music_muted = state.musicMuted
if state.has("sfxMuted"):
sfx_muted = state.sfxMuted
if state.has("currentMusic"):
current_music = state.currentMusic
# Emit signals about changes
music_volume_changed.emit(music_volume)
sfx_volume_changed.emit(sfx_volume)
music_muted_changed.emit(music_muted)
sfx_muted_changed.emit(sfx_muted)
# Called when ReactSoundBridge reports it's ready
func _on_react_audio_ready():
print("ReactSoundBridge reports ready")
audio_initialized = true
# Update local state from React
if react_sound_bridge != null:
if react_sound_bridge.get("music_volume") != null:
music_volume = react_sound_bridge.music_volume
if react_sound_bridge.get("sfx_volume") != null:
sfx_volume = react_sound_bridge.sfx_volume
if react_sound_bridge.get("music_muted") != null:
music_muted = react_sound_bridge.music_muted
if react_sound_bridge.get("sfx_muted") != null:
sfx_muted = react_sound_bridge.sfx_muted
if react_sound_bridge.get("current_music") != null:
current_music = react_sound_bridge.current_music
# Emit the audio ready signal
audio_ready.emit()
# Called when AudioBridge connects to the platform-one sound manager
func _on_audio_bridge_connected(is_connected: bool):
print("AudioBridge connected: ", is_connected)
if is_connected:
audio_initialized = true
# Request the sound state from the platform-one sound manager
if audio_bridge.has_method("get_sound_state"):
audio_bridge.get_sound_state()
# Emit the audio ready signal
audio_ready.emit()
# Called when any user interaction happens in web builds
func _on_user_interaction(_arg=null):
if OS.has_feature("web") and not audio_initialized:
_initialize_web_audio()
# Process input events directly
func _input(event):
if OS.has_feature("web") and not audio_initialized:
if event is InputEventMouseButton or event is InputEventKey:
if event.pressed:
_initialize_web_audio()
# If this method is called from JavaScript, it will help the game to
# initialize audio properly in web builds
func init_web_audio_from_js():
pass
#if OS.has_feature("web") and not audio_initialized:
#_initialize_web_audio()
# Initialize audio for web builds
func _initialize_web_audio():
if audio_initialized:
return
# For web builds, we notify JavaScript to initialize audio
# if OS.has_feature("web"):
# JSBridge.JavaScriptGlobal.handle_audio_action("INITIALIZE_AUDIO")
#
# # We don't need to create any dummy players, as JavaScript will handle the audio
# audio_initialized = true
# audio_ready.emit()
# return
# For non-web platforms, initialize Godot audio (this shouldn't get called)
# if not OS.has_feature("web"):
# Set the flag to prevent multiple initializations
audio_initialized = true
audio_ready.emit()
# Play background music
func play_music(sound_name: String, loop: bool = true):
if not audio_initialized:
return
# Store the current music name
current_music = sound_name
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to play music: ", sound_name)
# if audio_bridge.has_method("play_music") and audio_bridge.play_music(sound_name):
# return
#
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to play music: ", sound_name)
# JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_MUSIC", sound_name)
# return
# For native builds, use Godot audio
if not SOUND_FILES.has(sound_name):
return
# Get the file path
var file_path = SOUND_FILES[sound_name]
# Load the audio stream
var stream = load(file_path)
if stream == null:
return
# Stop current music if playing
if music_player.playing:
music_player.stop()
# Set up and play the music
music_player.stream = stream
music_player.bus = MUSIC_BUS_NAME
# Set looping if supported by the stream
if stream is AudioStreamMP3 or stream is AudioStreamOggVorbis:
stream.loop = loop
# Ensure volume is set correctly before playing
_apply_music_volume()
# Play the music
music_player.play()
# Play a sound effect
func play_sfx(sound_name: String):
if not audio_initialized:
return
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#print("Using AudioBridge to play sfx: ", sound_name)
#if audio_bridge.has_method("play_sfx") and audio_bridge.play_sfx(sound_name):
#return
#
## Fall back to JavaScript Bridge
#print("Using JavaScriptBridge to play sfx: ", sound_name)
#JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_SFX", sound_name)
#return
# For native builds, use Godot audio
if not SOUND_FILES.has(sound_name):
return
# Get the file path
var file_path = SOUND_FILES[sound_name]
# Load the audio stream
var stream = load(file_path)
if stream == null:
return
# Create or reuse a player for this sound
var player: AudioStreamPlayer
if not sfx_players.has(sound_name):
player = AudioStreamPlayer.new()
add_child(player)
sfx_players[sound_name] = player
else:
player = sfx_players[sound_name]
if player.playing:
player.stop()
# Set up and play the sound
player.stream = stream
if sfx_muted:
player.volume_db = linear_to_db(0)
else:
player.volume_db = linear_to_db(sfx_volume)
player.bus = SFX_BUS_NAME
player.play()
# Stop background music
func stop_music():
if not audio_initialized:
return
# For web builds, try multiple bridge options
#if OS.has_feature("web"):
## Try AudioBridge first (platform-one integration)
#if audio_bridge != null and audio_bridge.get("is_connected") == true:
#print("Using AudioBridge to stop music")
#if audio_bridge.has_method("stop_music") and audio_bridge.stop_music():
#current_music = ""
#return
#
## Fall back to JavaScript Bridge
#print("Using JavaScriptBridge to stop music")
#JSBridge.JavaScriptGlobal.handle_audio_action("STOP_MUSIC")
#current_music = ""
#return
# For native builds, use Godot audio
if music_player and music_player.playing:
music_player.stop()
current_music = ""
# Set music volume (0.0 to 1.0)
func set_music_volume(volume: float):
music_volume = clampf(volume, 0.0, 1.0)
# For web builds, try multiple bridge options
if false:
pass
# if OS.has_feature("web"):
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to set music volume: ", music_volume)
# if audio_bridge.has_method("set_music_volume"):
# audio_bridge.set_music_volume(music_volume)
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to set music volume: ", music_volume)
# JSBridge.JavaScriptGlobal.handle_audio_action("SET_MUSIC_VOLUME", "", music_volume)
else:
# Apply to local Godot audio system
_apply_music_volume()
# Emit signal
music_volume_changed.emit(music_volume)
# Set SFX volume (0.0 to 1.0)
func set_sfx_volume(volume: float):
sfx_volume = clampf(volume, 0.0, 1.0)
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to set sfx volume: ", sfx_volume)
# if audio_bridge.has_method("set_sfx_volume"):
# audio_bridge.set_sfx_volume(sfx_volume)
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to set sfx volume: ", sfx_volume)
# JSBridge.JavaScriptGlobal.handle_audio_action("SET_SFX_VOLUME", "", sfx_volume)
else:
# Apply to local Godot audio system
_apply_sfx_volume()
# Emit signal
sfx_volume_changed.emit(sfx_volume)
# Toggle music mute state
func toggle_music_mute():
music_muted = !music_muted
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to toggle music mute: ", music_muted)
# if audio_bridge.has_method("toggle_music_mute"):
# audio_bridge.toggle_music_mute()
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to toggle music mute: ", music_muted)
# JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_MUSIC_MUTE")
else:
# Apply to local Godot audio system
_apply_music_volume()
# Emit signal
music_muted_changed.emit(music_muted)
# Toggle SFX mute state
func toggle_sfx_mute():
sfx_muted = !sfx_muted
# For web builds, try multiple bridge options
# if OS.has_feature("web"):
if false:
pass
# # Try AudioBridge first (platform-one integration)
# if audio_bridge != null and audio_bridge.get("is_connected") == true:
# print("Using AudioBridge to toggle sfx mute: ", sfx_muted)
# if audio_bridge.has_method("toggle_sfx_mute"):
# audio_bridge.toggle_sfx_mute()
# else:
# # Fall back to JavaScript Bridge
# print("Using JavaScriptBridge to toggle sfx mute: ", sfx_muted)
# JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_SFX_MUTE")
else:
# Apply to local Godot audio system
_apply_sfx_volume()
# Emit signal
sfx_muted_changed.emit(sfx_muted)
# Apply music volume settings
func _apply_music_volume():
if not audio_initialized:
return
var db = linear_to_db(music_volume)
print("SoundManager: Applying music volume: ", music_volume, " (", db, "dB)")
if music_bus_index != -1:
AudioServer.set_bus_mute(music_bus_index, music_muted)
if not music_muted:
AudioServer.set_bus_volume_db(music_bus_index, db)
if music_player != null:
music_player.volume_db = -80.0 if music_muted else db
# Apply SFX volume settings
func _apply_sfx_volume():
if not audio_initialized:
return
var db = linear_to_db(sfx_volume)
print("SoundManager: Applying SFX volume: ", sfx_volume, " (", db, "dB)")
if sfx_bus_index != -1:
AudioServer.set_bus_mute(sfx_bus_index, sfx_muted)
if not sfx_muted:
AudioServer.set_bus_volume_db(sfx_bus_index, db)
for player in sfx_players.values():
if player != null:
player.volume_db = -80.0 if sfx_muted else db
# Helper function to convert linear volume to decibels
func linear_to_db(linear_value: float) -> float:
if linear_value <= 0:
return -80.0
return 20.0 * log(linear_value) / log(10.0)
func _load_settings():
print("SoundManager: Loading settings from ", CONFIG_PATH)
var config = ConfigFile.new()
var err = config.load(CONFIG_PATH)
if err == OK:
print("SoundManager: Successfully loaded settings file")
# Load saved values
music_volume = config.get_value("audio", "music_volume", 0.8)
sfx_volume = config.get_value("audio", "sfx_volume", 0.8)
music_muted = config.get_value("audio", "music_muted", false)
sfx_muted = config.get_value("audio", "sfx_muted", false)
else:
print("SoundManager: No settings file found, using defaults")
# Set default values
music_volume = 0.8
sfx_volume = 0.8
music_muted = false
sfx_muted = false
# Save the default values
_save_settings()
print("SoundManager: Loaded values - Music: ", music_volume, " SFX: ", sfx_volume)
# Apply the loaded settings immediately
_apply_music_volume()
_apply_sfx_volume()
func _save_settings():
print("SoundManager: Saving settings...")
var config = ConfigFile.new()
# Save current values
config.set_value("audio", "music_volume", music_volume)
config.set_value("audio", "sfx_volume", sfx_volume)
config.set_value("audio", "music_muted", music_muted)
config.set_value("audio", "sfx_muted", sfx_muted)
# Save to file
var err = config.save(CONFIG_PATH)
if err == OK:
print("SoundManager: Settings saved successfully")
else:
print("SoundManager: Error saving settings: ", err)
func get_music_volume() -> float:
if music_muted:
return MIN_VOLUME
return music_volume
func get_sfx_volume() -> float:
if sfx_muted:
return MIN_VOLUME
return sfx_volume

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

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

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

@ -5,27 +5,38 @@ var camera_rotation:Vector3
@onready var camera = $Camera
# Plane for mouse intersection
var ground_plane := Plane(Vector3.UP, 0)
func _ready():
camera_rotation = rotation_degrees # Initial rotation
pass
func _process(delta):
# Set position and rotation to targets
position = position.lerp(camera_position, delta * 8)
rotation_degrees = rotation_degrees.lerp(camera_rotation, delta * 6)
handle_input(delta)
# Handle input
func get_mouse_world_position() -> Vector3:
var mouse_pos = get_viewport().get_mouse_position()
var from = camera.project_ray_origin(mouse_pos)
var dir = camera.project_ray_normal(mouse_pos)
var intersection = ground_plane.intersects_ray(from, dir)
if intersection:
return intersection
# If no intersection, return a point in front of the camera
return camera.global_position + (-camera.global_transform.basis.z * 10)
func _input(event):
# Rotate camera using mouse (hold 'middle' mouse button)
if event is InputEventMouseMotion:
if Input.is_action_pressed("camera_rotate"):
camera_rotation += Vector3(0, -event.relative.x / 10, 0)
func handle_input(_delta):
# Rotation
var input := Vector3.ZERO
input.x = Input.get_axis("camera_left", "camera_right")
@ -36,14 +47,5 @@ func handle_input(_delta):
camera_position += input / 4
# Back to center
if Input.is_action_pressed("camera_center"):
camera_position = Vector3()
func _input(event):
# Rotate camera using mouse (hold 'middle' mouse button)
if event is InputEventMouseMotion:
if Input.is_action_pressed("camera_rotate"):
camera_rotation += Vector3(0, -event.relative.x / 10, 0)

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

@ -0,0 +1,85 @@
extends Node
const SAVE_KEY = "stem_city_save_data"
func save_map(map: DataMap) -> void:
if OS.has_feature("web"):
# For web builds, use localStorage
var save_data = {
"cash": map.cash,
"structures": []
}
# Convert structures to a format that can be serialized
for structure in map.structures:
save_data.structures.append({
"position": {"x": structure.position.x, "y": structure.position.y},
"orientation": structure.orientation,
"structure": structure.structure
})
# Convert to JSON and save to localStorage
var json = JSON.stringify(save_data)
print("Saving game data: ", json) # Debug log
# Try to save to localStorage
var result = JavaScriptBridge.eval("""
try {
localStorage.setItem('%s', '%s');
return 'success';
} catch(e) {
console.error('Error saving game:', e);
return 'error:' + e.message;
}
""" % [SAVE_KEY, json])
if result != "success":
push_error("Failed to save game: " + result)
else:
# For desktop builds, use the existing ResourceSaver
var result = ResourceSaver.save(map, "user://map.res")
if result != OK:
push_error("Failed to save game: " + str(result))
func load_map() -> DataMap:
if OS.has_feature("web"):
# For web builds, load from localStorage
var map = DataMap.new()
var json = JavaScriptBridge.eval("""
try {
return localStorage.getItem('%s');
} catch(e) {
console.error('Error loading game:', e);
return null;
}
""" % SAVE_KEY)
print("Loaded game data: ", json) # Debug log
if json and json != "null":
var save_data = JSON.parse_string(json)
if save_data:
map.cash = save_data.cash
map.structures.clear()
for structure_data in save_data.structures:
var data_structure = DataStructure.new()
data_structure.position = Vector2i(structure_data.position.x, structure_data.position.y)
data_structure.orientation = structure_data.orientation
data_structure.structure = structure_data.structure
map.structures.append(data_structure)
else:
push_error("Failed to parse save data")
else:
print("No save data found, starting new game")
return map
else:
# For desktop builds, use the existing ResourceLoader
if ResourceLoader.exists("user://map.res"):
var map = ResourceLoader.load("user://map.res")
if map:
return map
else:
push_error("Failed to load map resource")
return DataMap.new()

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