Work for loading missions.

pull/18/head
jc-stemuli 2025-05-02 20:27:20 +07:00
parent 20d631de6f
commit 6055d88d5e
113 changed files with 2540 additions and 1553 deletions

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

@ -0,0 +1,62 @@
[gd_resource type="Resource" script_class="MissionConfig" load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/mission/mission_config.gd" id="1_config"]
[resource]
script = ExtResource("1_config")
starting_structures = ["res://structures/road-straight.tres", "res://structures/building-small-a.tres"]
initial_structures = [
{
"structure_path": "res://structures/road-straight.tres",
"position": Vector3(0, 0, 0),
"rotation": 0
},
{
"structure_path": "res://structures/road-straight.tres",
"position": Vector3(1, 0, 0),
"rotation": 90
}
]
missions = [
{
"id": "custom_mission_1",
"title": "Road Network",
"description": "Build a basic road network for your city",
"objectives": [
{
"type": 1,
"target_count": 4,
"structure_path": "res://structures/road-straight.tres",
"description": "Build 4 straight roads"
}
],
"rewards": {
"cash": 200,
"experience": 25
},
"unlocked_items": [
"res://structures/road-corner.tres"
]
},
{
"id": "custom_mission_2",
"title": "First Houses",
"description": "Start building houses along your roads",
"objectives": [
{
"type": 1,
"target_count": 2,
"structure_path": "res://structures/building-small-a.tres",
"description": "Build 2 Type A houses"
}
],
"rewards": {
"cash": 300,
"experience": 50
},
"unlocked_items": [
"res://structures/building-small-b.tres",
"res://structures/building-small-c.tres"
]
}
]

@ -23,7 +23,6 @@ allow_system_fallback=true
force_autohinter=false
hinting=1
subpixel_positioning=1
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]

@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://x5h4xutbldq3"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="1_nv6c6"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_yfbrc"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_yfbrc"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_d0ffl"]
[sub_resource type="Resource" id="Resource_ywws1"]

@ -1,12 +1,12 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cjr36hqnmyn0x"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_dhx01"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="1_dhx01"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_em5vq"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_mum3p"]
[sub_resource type="Resource" id="Resource_7c02e"]
script = ExtResource("1_dhx01")
type = 0
type = 1
target_count = 3
current_count = 0
description = "Build 3 residential buildings"

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

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

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://442cwthak2pa"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_tcpuf"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_tcpuf"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="2_d7dn8"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_viy3t"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_viy3t"]
[sub_resource type="Resource" id="Resource_1rxby"]
script = ExtResource("1_tcpuf")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://bom5bu47dy5kp"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_pfd6v"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_pfd6v"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_ktjdq"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_w1cq1"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_w1cq1"]
[sub_resource type="Resource" id="Resource_a42kn"]
script = ExtResource("1_pfd6v")

@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=4 format=3 uid="uid://csrqvfwp63ygr"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_a8iy6"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_5q4dp"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_a8iy6"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="2_5q4dp"]
[sub_resource type="Resource" id="Resource_m1jev"]
script = ExtResource("1_a8iy6")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://qwiwim2pg88f"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_lsd8i"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_lsd8i"]
[ext_resource type="Resource" uid="uid://mxrnqinnsqnt" path="res://structures/road-straight-lightposts.tres" id="2_v2q1j"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_gtu3s"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_gtu3s"]
[sub_resource type="Resource" id="Resource_mhw3l"]
script = ExtResource("1_lsd8i")

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

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cfgw8dblm55c5"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_fvshb"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_fvshb"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_evy85"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_mtrpd"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_mtrpd"]
[sub_resource type="Resource" id="Resource_pj2vg"]
script = ExtResource("1_fvshb")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://ba3ndftq7dht7"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_miqck"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_miqck"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_r3vpw"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_mdu3m"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_mdu3m"]
[sub_resource type="Resource" id="Resource_utswr"]
script = ExtResource("1_miqck")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://dgimr2v12rjqu"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_8rvyb"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_8rvyb"]
[ext_resource type="Resource" uid="uid://c4qbn3d85prxx" path="res://structures/power-plant.tres" id="2_0kbkj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_8cjg3"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_8cjg3"]
[sub_resource type="Resource" id="Resource_fmshv"]
script = ExtResource("1_8rvyb")

@ -1,9 +1,9 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=7 format=3 uid="uid://dm2o4dq2oml53"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_x4c68"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_x4c68"]
[ext_resource type="Resource" uid="uid://tm532uesguhk" path="res://structures/grass.tres" id="2_ckbw6"]
[ext_resource type="Resource" uid="uid://y6jafhfnhbrp" path="res://structures/grass-trees-tall.tres" id="3_i0070"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_mjpfs"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_mjpfs"]
[sub_resource type="Resource" id="Resource_mldwf"]
script = ExtResource("1_x4c68")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://btwrfq37q8vey"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_ajvs5"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_ajvs5"]
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="2_exdai"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_kexcf"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_kexcf"]
[sub_resource type="Resource" id="Resource_qykcx"]
script = ExtResource("1_ajvs5")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cf7gpb4j7gq1g"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_8k2sf"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_8k2sf"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_4a8uo"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_aihyv"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_aihyv"]
[sub_resource type="Resource" id="Resource_fmsjh"]
script = ExtResource("1_8k2sf")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://ddmxjjyxgxyxo"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_1hl01"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_1hl01"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_i3hgc"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_6fq1p"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_6fq1p"]
[sub_resource type="Resource" id="Resource_h6f0e"]
script = ExtResource("1_1hl01")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://doxd30r8qbgdq"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_kdkdo"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_kdkdo"]
[ext_resource type="Resource" uid="uid://mxrnqinnsqnt" path="res://structures/road-straight-lightposts.tres" id="2_0dtbf"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_uodr7"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_uodr7"]
[sub_resource type="Resource" id="Resource_uq2hw"]
script = ExtResource("1_kdkdo")

@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=4 format=3 uid="uid://duaxn13myfx22"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_40kcw"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_x8j3r"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_40kcw"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="2_x8j3r"]
[sub_resource type="Resource" id="Resource_qlixo"]
script = ExtResource("1_40kcw")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://fuxb3pfbbwjm"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_75i2t"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_75i2t"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_lh8y5"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="4_30pdy"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="4_30pdy"]
[sub_resource type="Resource" id="Resource_qb5w1"]
script = ExtResource("1_75i2t")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://bsic030rpgh08"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_75i2t"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_75i2t"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="2_3t66o"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_30pdy"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_30pdy"]
[sub_resource type="Resource" id="Resource_vcbou"]
script = ExtResource("1_75i2t")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://byd5jxiutxpky"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_3s3sm"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_3s3sm"]
[ext_resource type="Resource" uid="uid://c4qbn3d85prxx" path="res://structures/power-plant.tres" id="2_5l1dw"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_w8jc7"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_w8jc7"]
[sub_resource type="Resource" id="Resource_d7jw4"]
script = ExtResource("1_3s3sm")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://daug1o7kppqit"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_oa16m"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_oa16m"]
[ext_resource type="Resource" uid="uid://y6jafhfnhbrp" path="res://structures/grass-trees-tall.tres" id="2_x4xh6"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_3tpnw"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_3tpnw"]
[sub_resource type="Resource" id="Resource_8dxyl"]
script = ExtResource("1_oa16m")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cp7tcpktwlrkt"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_fywct"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_fywct"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="2_a71iq"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_38y5t"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_38y5t"]
[sub_resource type="Resource" id="Resource_wq8mf"]
script = ExtResource("1_fywct")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://c3q1afcvwi4rk"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_0s0wv"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_0s0wv"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_6f04c"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_7jlc0"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_7jlc0"]
[sub_resource type="Resource" id="Resource_3fy3g"]
script = ExtResource("1_0s0wv")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://ct1k7n2oopwdu"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_40klq"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_40klq"]
[ext_resource type="Resource" uid="uid://mxrnqinnsqnt" path="res://structures/road-straight-lightposts.tres" id="2_u46hd"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_j3mtn"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_j3mtn"]
[sub_resource type="Resource" id="Resource_fai8r"]
script = ExtResource("1_40klq")

@ -1,9 +1,9 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=7 format=3 uid="uid://d1fykuxfmh2q1"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_wnlf0"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_wnlf0"]
[ext_resource type="Resource" uid="uid://dqqe3iofnleup" path="res://structures/pavement-fountain.tres" id="2_bmmdj"]
[ext_resource type="Resource" uid="uid://y6jafhfnhbrp" path="res://structures/grass-trees-tall.tres" id="3_fegi2"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="4_8gmvv"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="4_8gmvv"]
[sub_resource type="Resource" id="Resource_1ht3m"]
script = ExtResource("1_wnlf0")

@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=4 format=3 uid="uid://detwnqsq87r30"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_cjqfg"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="2_pv8r1"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_cjqfg"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="2_pv8r1"]
[sub_resource type="Resource" id="Resource_e2jx7"]
script = ExtResource("1_cjqfg")

@ -1,9 +1,9 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=7 format=3 uid="uid://bj7tjuknfaeyg"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_t87pd"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_t87pd"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_ys3mw"]
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="3_jl18p"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="4_2h1ol"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="4_2h1ol"]
[sub_resource type="Resource" id="Resource_mht7j"]
script = ExtResource("1_t87pd")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://ctyrlnq5cxuiu"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_r0j2r"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_r0j2r"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="2_oqy42"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_r78ev"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_r78ev"]
[sub_resource type="Resource" id="Resource_c21kt"]
script = ExtResource("1_r0j2r")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://bwrkqv42wk8f"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_1k37f"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_1k37f"]
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="2_qlnvj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_4udbq"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_4udbq"]
[sub_resource type="Resource" id="Resource_0fxto"]
script = ExtResource("1_1k37f")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://d0nblitd4ixir"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_vdhxy"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_vdhxy"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="2_hh0xb"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_k5ivn"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_k5ivn"]
[sub_resource type="Resource" id="Resource_21csl"]
script = ExtResource("1_vdhxy")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cxh8dgf54oimx"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_c76o5"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_c76o5"]
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="2_4rkoa"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_iqaae"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_iqaae"]
[sub_resource type="Resource" id="Resource_r01e3"]
script = ExtResource("1_c76o5")

@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cpfr2xnjtpcog"]
[ext_resource type="Script" uid="uid://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="1_4b0uw"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_4b0uw"]
[ext_resource type="Resource" uid="uid://ccb475jeg7ym5" path="res://structures/grass-trees.tres" id="2_y8alv"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="3_p34yh"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="3_p34yh"]
[sub_resource type="Resource" id="Resource_grdmr"]
script = ExtResource("1_4b0uw")

@ -18,7 +18,6 @@ nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -18,7 +18,6 @@ nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true

@ -13,7 +13,7 @@ config_version=5
config/name="Starter Kit City Builder"
config/tags=PackedStringArray("starterkit")
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.4", "Forward Plus")
config/features=PackedStringArray("4.3", "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"
@ -28,6 +28,7 @@ SoundManager="*res://scripts/sound_manager.gd"
EventBus="*res://global/event_bus.gd"
Globals="*res://global/globals.gd"
WebSaveManager="*res://scripts/web_save_manager.gd"
JSBridge="*res://scripts/javascript_bridge.gd"
[display]
@ -94,12 +95,12 @@ camera_rotate={
}
structure_next={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":true,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
structure_previous={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":true,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
save={

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

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

@ -1,42 +1,7 @@
[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
[gd_scene load_steps=4 format=3 uid="uid://b4k3xfm8pd8qw"]
[ext_resource type="Script" path="res://scripts/building_selector.gd" id="1_jybm7"]
[ext_resource type="Resource" path="res://resources/building_selector.tres" id="2_resource"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
@ -50,184 +15,209 @@ corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[node name="BuildingSelector" type="Control"]
layout_mode = 3
[node name="BuildingSelector" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
script = ExtResource("1_yvr4p")
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
script = ExtResource("1_jybm7")
resource_data = ExtResource("2_resource")
[node name="ClickBlocker" type="ColorRect" parent="."]
visible = false
z_index = 1000
layout_mode = 2
mouse_filter = 2
color = Color(0, 0, 0, 0.01)
[node name="MainButton" type="Button" parent="."]
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"
custom_minimum_size = Vector2(30, 30)
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
mouse_filter = 2
text = "▶"
flat = true
[node name="SelectionPanel" type="Panel" parent="."]
layout_mode = 0
offset_left = 20.0
offset_top = 70.0
offset_right = 380.0
offset_bottom = 620.0
mouse_filter = 1
visible = false
layout_mode = 2
mouse_filter = 2
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
[node name="MarginContainer" type="MarginContainer" parent="SelectionPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 10
[node name="HBoxContainer" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
[node name="TitleLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
text = "Building Selector"
[node name="CloseButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 2
text = "✕"
flat = true
[node name="SearchBar" type="LineEdit" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
placeholder_text = "Search structures..."
[node name="FilterButtons" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 5
[node name="AllButton" type="Button" parent="SelectionPanel/FilterButtons"]
[node name="AllButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/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")
mouse_filter = 2
toggle_mode = true
button_pressed = true
text = "All"
flat = true
[node name="ResidentialButton" type="Button" parent="SelectionPanel/FilterButtons"]
[node name="GroundButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/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")
mouse_filter = 2
toggle_mode = true
text = "Residential"
text = "Ground"
flat = true
[node name="CommercialButton" type="Button" parent="SelectionPanel/FilterButtons"]
[node name="BuildingButton" type="Button" parent="SelectionPanel/MarginContainer/VBoxContainer/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")
mouse_filter = 2
toggle_mode = true
text = "Commercial"
text = "Buildings"
flat = true
[node name="IndustrialButton" type="Button" parent="SelectionPanel/FilterButtons"]
[node name="ScrollContainer" type="ScrollContainer" parent="SelectionPanel/MarginContainer/VBoxContainer"]
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
size_flags_vertical = 3
mouse_filter = 2
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/ScrollContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 20
mouse_filter = 2
[node name="GroundSection" type="VBoxContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer"]
[node name="GroundSection" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
mouse_filter = 2
[node name="Label" type="Label" parent="SelectionPanel/ScrollContainer/VBoxContainer/GroundSection"]
[node name="Label" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Ground Types"
horizontal_alignment = 1
theme_override_font_sizes/font_size = 16
text = "Ground Structures"
[node name="GroundOptions" type="GridContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer/GroundSection"]
[node name="GroundOptions" type="GridContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 2
columns = 4
[node name="BuildingSection" type="VBoxContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer"]
[node name="BuildingSection" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
mouse_filter = 2
[node name="Label" type="Label" parent="SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection"]
[node name="Label" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Buildings"
horizontal_alignment = 1
theme_override_font_sizes/font_size = 16
text = "Building Structures"
[node name="BuildingOptions" type="GridContainer" parent="SelectionPanel/ScrollContainer/VBoxContainer/BuildingSection"]
[node name="BuildingOptions" type="GridContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 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
columns = 4
[node name="DescriptionPanel" type="Panel" parent="SelectionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/DescriptionPanel"]
[node name="MarginContainer" type="MarginContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="VBoxContainer" type="VBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 10
[node name="TitleLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
[node name="TitleLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "No Building Selected"
horizontal_alignment = 1
theme_override_font_sizes/font_size = 18
text = "Structure Name"
[node name="DescriptionLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
[node name="DescriptionLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/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
text = "Structure description goes here..."
autowrap_mode = 2
[node name="StatsContainer" type="HBoxContainer" parent="SelectionPanel/DescriptionPanel/VBoxContainer"]
[node name="StatsContainer" type="HBoxContainer" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 20
[node name="PriceLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
[node name="PriceLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Price: $0"
[node name="PopulationLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
[node name="PopulationLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Population: 0"
[node name="PowerLabel" type="Label" parent="SelectionPanel/DescriptionPanel/VBoxContainer/StatsContainer"]
[node name="PowerLabel" type="Label" parent="SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Power: 0 kW"
text = "Power: 0"
[connection signal="pressed" from="MainButton" to="." method="_on_main_button_pressed"]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/HBoxContainer/CloseButton" to="." method="hide_panel"]
[connection signal="text_changed" from="SelectionPanel/MarginContainer/VBoxContainer/SearchBar" to="." method="_on_search_text_changed"]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/AllButton" to="." method="_on_filter_button_pressed" binds= ["All"]]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/GroundButton" to="." method="_on_filter_button_pressed" binds= ["Ground"]]
[connection signal="pressed" from="SelectionPanel/MarginContainer/VBoxContainer/FilterButtons/BuildingButton" to="." method="_on_filter_button_pressed" binds= ["Buildings"]]

@ -1,6 +1,6 @@
[gd_scene load_steps=8 format=3 uid="uid://b4gkfwf4i3ydl"]
[ext_resource type="Script" uid="uid://dexknr7it5val" path="res://scripts/mission/character_controller.gd" id="1_g3a7y"]
[ext_resource type="Script" path="res://scripts/mission/character_controller.gd" id="1_g3a7y"]
[ext_resource type="PackedScene" uid="uid://h2jjkvc4edsp" path="res://people/character-female-d.glb" id="2_d5jmi"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_b4w3n"]
@ -58,9 +58,9 @@ tracks/0/keys = {
[sub_resource type="AnimationLibrary" id="AnimationLibrary_8ib1s"]
_data = {
&"RESET": SubResource("Animation_reset"),
&"idle": SubResource("Animation_idle"),
&"walk": SubResource("Animation_walk")
"RESET": SubResource("Animation_reset"),
"idle": SubResource("Animation_idle"),
"walk": SubResource("Animation_walk")
}
[node name="Character" type="CharacterBody3D"]
@ -80,7 +80,7 @@ transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0, 0, 0)
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
&"": SubResource("AnimationLibrary_8ib1s")
"": SubResource("AnimationLibrary_8ib1s")
}
autoplay = "walk"
speed_scale = 3.0

@ -1,7 +1,7 @@
[gd_scene load_steps=4 format=3 uid="uid://bqjnp7uypupog"]
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="1_tnlhn"]
[ext_resource type="Script" uid="uid://drdr16kowbvmd" path="res://scripts/controls_panel.gd" id="1_xyuqg"]
[ext_resource type="Script" path="res://scripts/controls_panel.gd" id="1_xyuqg"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_f2mso"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)

@ -18,10 +18,10 @@ ambient_light_color = Color(0.662745, 0.694118, 0.772549, 1)
ambient_light_energy = 0.75
tonemap_mode = 2
ssao_enabled = true
ssao_radius = 0.25
ssao_intensity = 0.5
ssao_power = 100.0
ssao_horizon = 0.1
ssao_detail = 1.0
ssao_horizon = 0.25
ssao_light_affect = 0.5
ssao_ao_channel_affect = 0.75
sdfgi_cascades = 1
sdfgi_max_distance = 25.6
sdfgi_energy = 0.5

@ -1,47 +1,48 @@
[gd_scene load_steps=72 format=3 uid="uid://b6eb1v02n61vv"]
[gd_scene load_steps=73 format=3 uid="uid://b6eb1v02n61vv"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Script" 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" 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"]
[ext_resource type="Resource" uid="uid://ccb475jeg7ym5" path="res://structures/grass-trees.tres" id="6_fwsy4"]
[ext_resource type="Resource" uid="uid://y6jafhfnhbrp" path="res://structures/grass-trees-tall.tres" id="7_t5ykj"]
[ext_resource type="Script" uid="uid://d3n8yylhejdoh" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Script" path="res://scripts/view.gd" id="8_yovpv"]
[ext_resource type="Resource" uid="uid://tm532uesguhk" path="res://structures/grass.tres" id="9_e755i"]
[ext_resource type="Script" uid="uid://otnxt4e77gmc" path="res://scripts/mission/mission_manager.gd" id="10_oe3re"]
[ext_resource type="Script" 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://ch8vtcpine1lc" path="res://structures/pavement.tres" id="11_ro3en"]
[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="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="13_kf5aa"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://n26k1k7243dd" 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" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Resource" uid="uid://dqqe3iofnleup" path="res://structures/pavement-fountain.tres" id="14_t5ykj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://dveu4dnue0d54" path="res://structures/road-intersection.tres" id="15_e755i"]
[ext_resource type="Resource" uid="uid://x5h4xutbldq3" path="res://mission/unit_1.02/census_planning_1.tres" id="15_obmwc"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/unit_1.02/census_planning_2.tres" id="16_rl54y"]
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://mission/unit_1.02/census_planning_3.tres" id="17_rrdy6"]
[ext_resource type="Resource" uid="uid://cjr36hqnmyn0x" path="res://mission/unit_1.02/census_planning_3.tres" id="17_rrdy6"]
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="17_vlub6"]
[ext_resource type="PackedScene" uid="uid://b4gkfwf4i3ydl" path="res://scenes/character.tscn" id="18_8lrh8"]
[ext_resource type="PackedScene" uid="uid://cgk66f6rg03mj" path="res://scenes/hud.tscn" id="18_hud"]
[ext_resource type="PackedScene" uid="uid://bqjnp7uypupog" path="res://scenes/controls_panel.tscn" id="19_controls"]
[ext_resource type="Script" uid="uid://b5nubrdeseay4" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Script" path="res://scripts/game_manager.gd" id="20_game_manager"]
[ext_resource type="Resource" uid="uid://442cwthak2pa" path="res://mission/unit_1.02/market_research_1.tres" id="20_ngu16"]
[ext_resource type="PackedScene" uid="uid://b4s46k58ddpyc" path="res://scenes/sound_panel.tscn" id="21_sound_panel"]
[ext_resource type="Resource" uid="uid://dtal0tl2ee336" path="res://structures/store.tres" id="21_y11qv"]
[ext_resource type="PackedScene" uid="uid://btfwonjc8uj0w" path="res://scenes/mission_select_menu.tscn" id="24_ro3en"]
[ext_resource type="Resource" uid="uid://bom5bu47dy5kp" path="res://mission/unit_1.02/market_research_2.tres" id="24_xud6a"]
[ext_resource type="Resource" uid="uid://csrqvfwp63ygr" path="res://mission/unit_1.02/market_research_3.tres" id="25_6hx7u"]
[ext_resource type="PackedScene" uid="uid://b4k3xfm8pd8qw" path="res://scenes/building_selector.tscn" id="25_od4ux"]
[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://cua0khnbyusip" path="res://mission/unit_1.02/unlock_buildings.tres" id="28_ro3en"]
[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://bh148683scgge" path="res://mission/unit_1.02/construction_efficiency.tres" id="30_od4ux"]
[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"]
@ -61,7 +62,7 @@
[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://b8b67y260mq6g" path="res://scripts/mission/mission_objective.gd" id="51_kf5aa"]
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="51_kf5aa"]
[ext_resource type="Resource" uid="uid://bsic030rpgh08" path="res://mission/unit_1.06/sustainable_dev_2b.tres" id="57_e755i"]
[sub_resource type="Resource" id="Resource_1gdbm"]
@ -139,6 +140,7 @@ companion_dialog = {
}
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mission"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
@ -242,19 +244,26 @@ shadow_opacity = 0.75
[node name="HUD" parent="CanvasLayer" instance=ExtResource("18_hud")]
[node name="ControlsPanel" parent="CanvasLayer" instance=ExtResource("19_controls")]
visible = false
[node name="SoundPanel" parent="CanvasLayer" instance=ExtResource("21_sound_panel")]
visible = false
anchors_preset = 8
[node name="GenericTextPanel" parent="CanvasLayer" instance=ExtResource("13_7i6dj")]
visible = false
resource_data = ExtResource("14_76jlq")
[node name="MissionSelectMenu" parent="CanvasLayer" instance=ExtResource("24_ro3en")]
visible = false
[node name="BuildingSelector" parent="CanvasLayer" instance=ExtResource("25_od4ux")]
visible = false
offset_right = -277.0
[node name="MissionManager" type="Node" parent="." node_paths=PackedStringArray("mission_ui", "builder")]
script = ExtResource("10_oe3re")
missions = Array[ExtResource("14_vcrh8")]([ExtResource("15_obmwc"), ExtResource("16_rl54y"), ExtResource("17_rrdy6"), ExtResource("20_ngu16"), ExtResource("24_xud6a"), ExtResource("25_6hx7u"), ExtResource("26_lvk23"), ExtResource("27_s0e58"), ExtResource("28_hurxs"), ExtResource("29_rhn1n"), ExtResource("30_4rwkv"), ExtResource("31_j2idb"), ExtResource("32_ipu0c"), ExtResource("33_c0l5e"), ExtResource("34_21t20"), ExtResource("35_o0bjh"), ExtResource("36_2wodh"), ExtResource("37_psgx1"), ExtResource("38_hw762"), ExtResource("39_ymw5p"), ExtResource("40_uggp1"), ExtResource("41_f0dxf"), ExtResource("42_fv8gl"), ExtResource("43_qvne6"), ExtResource("44_haub2"), ExtResource("45_xs8xk"), SubResource("Resource_t5ykj"), ExtResource("47_6w4y8"), ExtResource("48_ck35a"), ExtResource("49_cvgxw"), ExtResource("50_6ke0d"), ExtResource("57_e755i")])
missions = Array[ExtResource("14_vcrh8")]([ExtResource("28_ro3en"), ExtResource("30_od4ux"), ExtResource("17_rrdy6"), ExtResource("20_ngu16"), ExtResource("24_xud6a"), ExtResource("25_6hx7u"), ExtResource("26_lvk23"), ExtResource("27_s0e58"), ExtResource("28_hurxs"), ExtResource("29_rhn1n"), ExtResource("30_4rwkv"), ExtResource("31_j2idb"), ExtResource("32_ipu0c"), ExtResource("33_c0l5e"), ExtResource("34_21t20"), ExtResource("35_o0bjh"), ExtResource("36_2wodh"), ExtResource("37_psgx1"), ExtResource("38_hw762"), ExtResource("39_ymw5p"), ExtResource("40_uggp1"), ExtResource("41_f0dxf"), ExtResource("42_fv8gl"), ExtResource("43_qvne6"), ExtResource("44_haub2"), ExtResource("45_xs8xk"), SubResource("Resource_t5ykj"), ExtResource("47_6w4y8"), ExtResource("48_ck35a"), ExtResource("49_cvgxw"), ExtResource("50_6ke0d"), ExtResource("57_e755i")])
mission_ui = NodePath("MissionPanel")
builder = NodePath("../Builder")
character_scene = ExtResource("18_8lrh8")
@ -444,6 +453,23 @@ 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)
[node name="Builder2" type="Node3D" parent="." node_paths=PackedStringArray("selector", "selector_container", "view_camera", "gridmap", "cash_display")]
script = ExtResource("1_jybm7")
structures = Array[ExtResource("2_54v6r")]([ExtResource("13_kf5aa"), ExtResource("5_v5o2m"), ExtResource("7_t5ykj"), ExtResource("6_fwsy4"), ExtResource("9_e755i"), ExtResource("14_t5ykj"), ExtResource("11_ro3en"), ExtResource("12_xtc7p"), ExtResource("15_e755i"), ExtResource("21_y11qv"), ExtResource("2_bwyku"), ExtResource("11_20frt")])
selector = NodePath("Selector")
selector_container = NodePath("Selector/Container")
view_camera = NodePath("../View/Camera")
gridmap = NodePath("../GridMap")
cash_display = NodePath("../CanvasLayer/HUD/HBoxContainer/CashItem/CashLabel")
[node name="Selector" type="Node3D" parent="Builder2"]
[node name="Sprite" type="Sprite3D" parent="Builder2/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="Builder2/Selector"]
[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,56 @@
[gd_scene load_steps=2 format=3 uid="uid://b6x8v0j6y5n3q"]
[ext_resource type="Script" path="res://scripts/mission/learning_panel.gd" id="1_2k4m3"]
[node name="LearningPanel" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_2k4m3")
[node name="PanelContainer" type="PanelContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 20
[node name="TitleLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Learning Panel"
horizontal_alignment = 1
[node name="ContentLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
text = "Welcome to the learning panel!"
autowrap_mode = 3
[node name="SubmitButtonContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="SubmitButton" type="Button" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/SubmitButtonContainer"]
layout_mode = 2
size_flags_horizontal = 4
text = "Submit"

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

@ -28,6 +28,69 @@ signal structure_placed(structure_index, position) # For our mission flow
var invalid_placement_material: StandardMaterial3D
# Central structure management
var _structures: Array[Structure] = []
# Getter for structures that ensures deduplication
func get_structures() -> Array[Structure]:
return _structures
# Setter for structures that ensures deduplication
func set_structures(new_structures: Array[Structure]) -> void:
print("\n=== Updating Structures ===")
_structures = new_structures
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = _structures
print("=== Structure Update Complete ===\n")
# Function to deduplicate structures
func _deduplicate_structures() -> void:
print("\n=== Deduplicating Structures ===")
# Create a new array to store unique structures
var unique_structures: Array[Structure] = []
var seen_paths = {}
# Initialize all structures, skipping duplicates
for i in range(_structures.size()):
var structure = _structures[i]
if not structure:
push_error("Null structure at index " + str(i))
continue
# Skip if no model
if not structure.model:
push_error("Structure at index " + str(i) + " has no model!")
continue
var path = structure.model.resource_path
# Skip if we've seen this path before
if path in seen_paths:
print("Skipping duplicate structure: " + path)
continue
seen_paths[path] = true
# Initialize unlocked property only if it doesn't exist
if not "unlocked" in structure:
structure.unlocked = false
print("Adding structure: " + path + " - Unlocked: " + str(structure.unlocked))
unique_structures.append(structure)
# Replace the original structures array with our deduplicated one
_structures.clear()
_structures.append_array(unique_structures)
# Print final structure list for verification
print("\nFinal structure list:")
for i in range(_structures.size()):
var structure = _structures[i]
print(str(i) + ": " + structure.model.resource_path + " - Unlocked: " + str(structure.unlocked))
print("=== Structure Deduplication Complete ===\n")
func _ready():
map = DataMap.new()
plane = Plane(Vector3.UP, Vector3.ZERO)
@ -48,29 +111,67 @@ func _ready():
# Give the construction manager references it needs
construction_manager.builder = self
construction_manager.gridmap = gridmap
construction_manager.structures = structures
# Set gridmap cell size to 3 units
if gridmap:
gridmap.cell_size = Vector3(3, 3, 3)
# Sound effects now handled in game_manager.gd
print("\n=== Initializing Structures ===")
# Load all structures from the structures directory
var dir = DirAccess.open("res://structures")
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if file_name.ends_with(".tres"):
var structure = load("res://structures/" + file_name)
if structure:
structures.append(structure)
file_name = dir.get_next()
else:
push_error("Failed to open structures directory!")
# Ensure we start with an unlocked structure
var found_unlocked = false
for i in range(structures.size()):
if "unlocked" in structures[i] and structures[i].unlocked:
index = i
found_unlocked = true
print("Starting with unlocked structure: " + structures[i].model.resource_path)
break
# Set initial structures through our centralized system
set_structures(structures)
if not found_unlocked:
print("WARNING: No unlocked structures found at start!")
# Connect to mission manager's structures_unlocked signal
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager:
mission_manager.structures_unlocked.connect(_on_structures_unlocked)
# Initially hide the selector since we don't know which structure to show yet
if selector:
selector.visible = false
print("=== Structure Initialization Complete ===\n")
update_structure()
update_cash()
# Override the setter for structures to ensure deduplication
func _set_structures(new_structures: Array[Structure]) -> void:
structures = new_structures
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
# Function to add a structure to the array
func add_structure(structure: Structure) -> void:
structures.append(structure)
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
# Function to remove a structure from the array
func remove_structure(structure: Structure) -> void:
structures.erase(structure)
_deduplicate_structures()
# Update construction manager with deduplicated structures
if construction_manager:
construction_manager.structures = structures
func _process(delta):
# Skip all building functionality if disabled or game is paused
if disabled or get_tree().paused:
@ -115,6 +216,13 @@ func is_mouse_over_ui() -> bool:
# Get mouse position
var mouse_pos = get_viewport().get_mouse_position()
# Check building selector panel
var building_selector = get_node_or_null("/root/Main/CanvasLayer/BuildingSelector")
if building_selector and building_selector.selection_panel and building_selector.selection_panel.visible:
var panel_rect = building_selector.selection_panel.get_global_rect()
if panel_rect.has_point(mouse_pos):
return true
# Let's try an extremely simple approach - just check coordinates
# most HUDs are at top of screen
if mouse_pos.y < 100:
@ -227,7 +335,7 @@ func action_build(gridmap_position, can_place: bool):
var is_terrain = structures[index].type == Structure.StructureType.TERRAIN
# Check if we're in mission 3 (when we should use construction workers)
var use_worker_construction = structures[index].spawn_builder
var use_worker_construction = true
var mission_manager = get_node_or_null("/root/Main/MissionManager")
# Sound effects are handled via game_manager.gd through the structure_placed signal
@ -262,20 +370,10 @@ func action_build(gridmap_position, can_place: bool):
# We still set the cell item for collision detection
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
elif is_residential or use_worker_construction:
# For residential buildings in mission 3, use construction workers
# Pass the current selector basis to preserve rotation
else:
# For all other buildings, start construction process with worker
var selector_basis = selector.basis
construction_manager.start_construction(gridmap_position, index, selector_basis)
# Don't place the building immediately - it will be placed when construction completes
# We leave gridmap empty for now
# For mission 3, don't update objectives immediately - wait for construction to finish
# See _update_mission_objective_on_completion in building_construction_manager.gd
else:
# For non-road structures or not in mission 3, add to the gridmap as usual
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
if previous_tile != index:
map.cash -= structures[index].price
@ -435,42 +533,61 @@ func action_rotate():
# Toggle between structures to build
func action_structure_toggle():
# Original keyboard controls
if Input.is_action_just_pressed("structure_next"):
# Find the next unlocked structure
var next_index = index
var tried_indices = []
print("\nE key pressed - attempting to switch to next structure")
# First, collect all unlocked structure indices in order
var unlocked_indices = []
for i in range(_structures.size()):
var structure = _structures[i]
if structure.model:
if structure.unlocked:
unlocked_indices.append(i)
while tried_indices.size() < structures.size():
next_index = wrap(next_index + 1, 0, structures.size())
if tried_indices.has(next_index):
break # We've already tried this index, avoid infinite loop
tried_indices.append(next_index)
if unlocked_indices.is_empty():
print("WARNING: No unlocked structures available!")
return
# Check if this structure is unlocked
if "unlocked" in structures[next_index] and structures[next_index].unlocked:
index = next_index
break
# Find the next unlocked structure
var current_pos = unlocked_indices.find(index)
if current_pos == -1:
# If current index is not in unlocked list, start from beginning
index = unlocked_indices[0]
print("Current structure not unlocked, starting from first unlocked: ", index)
else:
# Move to next structure, wrapping around to start if at end
index = unlocked_indices[(current_pos + 1) % unlocked_indices.size()]
print("Moving to next unlocked structure: ", index)
update_structure()
if Input.is_action_just_pressed("structure_previous"):
# Find the previous unlocked structure
var prev_index = index
var tried_indices = []
print("\nQ key pressed - attempting to switch to previous structure")
# First, collect all unlocked structure indices in order
var unlocked_indices = []
for i in range(_structures.size()):
var structure = _structures[i]
if structure.model:
if structure.unlocked:
unlocked_indices.append(i)
while tried_indices.size() < structures.size():
prev_index = wrap(prev_index - 1, 0, structures.size())
if tried_indices.has(prev_index):
break # We've already tried this index, avoid infinite loop
tried_indices.append(prev_index)
if unlocked_indices.is_empty():
print("WARNING: No unlocked structures available!")
return
# Check if this structure is unlocked
if "unlocked" in structures[prev_index] and structures[prev_index].unlocked:
index = prev_index
break
update_structure()
# Find the previous unlocked structure
var current_pos = unlocked_indices.find(index)
if current_pos == -1:
# If current index is not in unlocked list, start from end
index = unlocked_indices[-1]
print("Current structure not unlocked, starting from last unlocked: ", index)
else:
# Move to previous structure, wrapping around to end if at start
index = unlocked_indices[(current_pos - 1 + unlocked_indices.size()) % unlocked_indices.size()]
print("Moving to previous unlocked structure: ", index)
update_structure()
# Update the structure visual in the 'cursor'
func update_structure():
# Clear previous structure preview in selector
@ -478,14 +595,14 @@ func update_structure():
selector_container.remove_child(n)
# Create new structure preview in selector
var _model = structures[index].model.instantiate()
var _model = _structures[index].model.instantiate()
selector_container.add_child(_model)
# Get reference to the selector sprite
var selector_sprite = selector.get_node("Sprite")
# Apply appropriate scaling based on structure type
if structures[index].model.resource_path.contains("power_plant"):
if _structures[index].model.resource_path.contains("power_plant"):
# Scale power plant model to be much smaller (0.5x)
_model.scale = Vector3(0.5, 0.5, 0.5)
# Center the power plant model within the selector
@ -496,7 +613,7 @@ func update_structure():
_model.position.y += 0.0 # No need for Y adjustment with scaling
# Get the selector scale from the structure resource
var scale_factor = structures[index].selector_scale
var scale_factor = _structures[index].selector_scale
selector_sprite.scale = Vector3(scale_factor, scale_factor, scale_factor)
# Sound effects are now handled in game_manager.gd
@ -827,25 +944,13 @@ func _on_construction_completed(position: Vector3):
break
if structure_index >= 0:
# Add the completed building to the gridmap with the correct rotation and structure index
gridmap.set_cell_item(position, structure_index, rotation_index)
# Check if we need to spawn a character for mission 1
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager:
# We DON'T re-emit the structure_placed signal here, because we already
# emitted it when construction started in action_build()
# This prevents double-counting buildings in the HUD
# Now check if we need to manually handle mission 1 character spawning
if mission_manager.current_mission and mission_manager.current_mission.id == "1" and not mission_manager.character_spawned:
mission_manager.character_spawned = true
mission_manager._spawn_character_on_road(position)
# NOTE: We removed the structure_placed signal emission here to fix the population double-counting
else:
# We don't emit the signal anymore to prevent double-counting
pass
else:
# No residential building structure found
pass
@ -855,11 +960,6 @@ func _on_construction_completed(position: Vector3):
# Make sure the navigation mesh is updated
rebake_navigation_mesh()
# Note that mission objective updates are now handled in the construction manager
# to ensure they only occur after construction is complete
# Saving/load
@ -965,6 +1065,7 @@ func check_can_place(pos: Vector3) -> bool:
if construction_manager:
for site_pos in construction_manager.construction_sites:
var distance = Vector2(abs(site_pos.x - pos.x), abs(site_pos.z - pos.z))
# Block the entire 3x3 grid space where construction is happening
if distance.x < 3 and distance.y < 3:
return false
@ -977,6 +1078,7 @@ func check_can_place(pos: Vector3) -> bool:
float(child.name.split("_")[2])
)
var distance = Vector2(abs(plot_pos.x - pos.x), abs(plot_pos.z - pos.z))
# Block the entire 3x3 grid space where construction is happening
if distance.x < 3 and distance.y < 3:
return false
@ -1021,3 +1123,95 @@ func _get_all_mesh_instances(node: Node) -> Array:
mesh_instances.append_array(_get_all_mesh_instances(child))
return mesh_instances
# Place a structure at a specific position and rotation
func place_structure(structure_index: int, position: Vector3, rotation: float = 0.0) -> void:
if structure_index < 0 or structure_index >= structures.size():
push_error("Invalid structure index: " + str(structure_index))
return
# Store current index
var previous_index = index
# Set the structure to place
index = structure_index
# Create a gridmap position from the world position
var gridmap_position = Vector3(
round(position.x / 3.0) * 3.0,
0,
round(position.z / 3.0) * 3.0
)
# Check if we can place here
if not check_can_place(gridmap_position):
push_error("Cannot place structure at position: " + str(position))
index = previous_index
return
# Place the structure
var is_road = structures[index].type == Structure.StructureType.ROAD
var is_residential = structures[index].type == Structure.StructureType.RESIDENTIAL_BUILDING
var is_power_plant = structures[index].model.resource_path.contains("power_plant")
var is_terrain = structures[index].type == Structure.StructureType.TERRAIN
if is_road:
# For roads, we'll need to track in our data without using the GridMap
# But for now, we won't add it to the GridMap visually, just add to NavRegion3D
var previous_tile = gridmap.get_cell_item(gridmap_position)
if previous_tile >= 0 and previous_tile < structures.size() and structures[previous_tile].type == Structure.StructureType.ROAD:
_remove_road_from_navregion(gridmap_position)
_add_road_to_navregion(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_residential:
# For residential buildings, we use the construction manager
construction_manager.start_construction(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_power_plant:
# For power plants, we just place them directly in the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
elif is_terrain:
# For grass and trees (terrain), we just place them directly in the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
else:
# For other structures, we use the gridmap
gridmap.set_cell_item(gridmap_position, index)
emit_signal("structure_placed", index, gridmap_position)
# Restore previous index
index = previous_index
# New function to handle when structures are unlocked
func _on_structures_unlocked():
print("\n=== Structures Unlocked, Setting Initial Structure ===")
# Find the first unlocked structure in the array
var found_unlocked = false
print("\nChecking for unlocked structures:")
for i in range(_structures.size()):
var structure = _structures[i]
print("Structure " + str(i) + ": " + structure.model.resource_path + " - Unlocked: " + str(structure.unlocked))
if structure.unlocked:
index = i
found_unlocked = true
print("Found first unlocked structure at index " + str(i) + ": " + structure.model.resource_path)
break
if not found_unlocked:
print("WARNING: No unlocked structures found!")
# Don't set any structure as selected if none are unlocked
index = -1
# Hide the selector since we have no structures to place
if selector:
selector.visible = false
else:
# Show the selector since we have a structure to place
if selector:
selector.visible = true
update_structure()
print("=== Initial Structure Set ===\n")
# Function to deduplicate structures

@ -1,64 +1,202 @@
extends Control
extends PanelContainer
@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
const GenericText = preload("res://resources/generic_text_panel.resource.gd")
@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
signal closed
var _builder: Node
@export var resource_data: GenericText
@onready var main_button: Button = $MainButton
@onready var selection_panel: Panel = $SelectionPanel
@onready var ground_options = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection/GroundOptions
@onready var building_options = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection/BuildingOptions
@onready var search_bar: LineEdit = $SelectionPanel/MarginContainer/VBoxContainer/SearchBar
@onready var filter_buttons: HBoxContainer = $SelectionPanel/MarginContainer/VBoxContainer/FilterButtons
@onready var description_panel: Panel = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel
@onready var title_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/TitleLabel
@onready var description_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/DescriptionLabel
@onready var price_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PriceLabel
@onready var population_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PopulationLabel
@onready var power_label: Label = $SelectionPanel/MarginContainer/VBoxContainer/DescriptionPanel/MarginContainer/VBoxContainer/StatsContainer/PowerLabel
@onready var click_blocker: ColorRect = $ClickBlocker
var builder: Node
var current_selection: int = 0
var is_panel_visible: bool = false
var current_filter: String = "All"
var search_text: String = ""
var slide_tween: Tween
var tween: Tween
func _ready():
# Connect the main button signal
main_button.pressed.connect(_on_main_button_pressed)
func _ready() -> void:
# Hide the panel initially
visible = false
# Make sure this control blocks mouse input from passing through
mouse_filter = Control.MOUSE_FILTER_STOP
# Initialize the panel state
if selection_panel:
selection_panel.visible = false
selection_panel.position = Vector2(0, 0) # Reset position
selection_panel.mouse_filter = Control.MOUSE_FILTER_STOP
selection_panel.size = Vector2(300, get_viewport_rect().size.y) # Set a fixed width
# Connect search bar signal
search_bar.text_changed.connect(_on_search_text_changed)
# Set up click blocker
if click_blocker:
click_blocker.visible = false
click_blocker.mouse_filter = Control.MOUSE_FILTER_STOP
click_blocker.size = get_viewport_rect().size
click_blocker.position = Vector2.ZERO
click_blocker.z_index = 1000 # Ensure it's above everything else
# Create background panel for the main button
var button_bg = ColorRect.new()
button_bg.color = Color(0, 0, 0, 0.7) # Semi-transparent black
button_bg.size = Vector2(40, 40) # Slightly larger than the button
button_bg.position = Vector2(-5, -5) # Offset to center the button
button_bg.mouse_filter = Control.MOUSE_FILTER_STOP # Block clicks on background
add_child(button_bg)
button_bg.z_index = -1 # Place behind the button
if main_button:
main_button.text = ""
main_button.mouse_filter = Control.MOUSE_FILTER_STOP
# Set initial position to stick to the right side
main_button.position.x = 0
# Center the button vertically
main_button.position.y = get_viewport_rect().size.y / 2 - 20
# Style the button
main_button.add_theme_color_override("font_color", Color(1, 1, 1)) # White text
main_button.add_theme_font_size_override("font_size", 24) # Larger font
# Connect signals
if main_button and not main_button.pressed.is_connected(_on_main_button_pressed):
main_button.pressed.connect(_on_main_button_pressed)
if search_bar and not search_bar.text_changed.is_connected(_on_search_text_changed):
search_bar.text_changed.connect(_on_search_text_changed)
# Connect filter button signals
for button in filter_buttons.get_children():
if button is Button:
if button is Button and not button.pressed.is_connected(_on_filter_button_pressed.bind(button.text)):
button.pressed.connect(_on_filter_button_pressed.bind(button.text))
# Initially hide the selection panel
selection_panel.visible = false
# Set mouse filters for containers
if ground_options:
ground_options.mouse_filter = Control.MOUSE_FILTER_STOP
if building_options:
building_options.mouse_filter = Control.MOUSE_FILTER_STOP
# 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 initial buttons if builder is set
if builder:
_create_filter_buttons()
_create_option_buttons()
# Create the building and ground option buttons
_create_option_buttons()
# Apply resource data if available
if resource_data:
apply_resource_data(resource_data)
func _create_option_buttons():
func show_panel() -> void:
visible = true
is_panel_visible = true
selection_panel.visible = true
click_blocker.visible = true
main_button.text = ""
# Create tween for smooth animation
if tween:
tween.kill()
tween = create_tween()
# Panel is opening
selection_panel.position.x = -selection_panel.size.x
tween.tween_property(selection_panel, "position:x", 0, 0.2)
tween.parallel().tween_property(main_button, "position:x", selection_panel.size.x, 0.2)
# Pause the game when the panel is open
get_tree().paused = true
func hide_panel() -> void:
is_panel_visible = false
# Create tween for smooth animation
if tween:
tween.kill()
tween = create_tween()
# Panel is closing
tween.tween_property(selection_panel, "position:x", -selection_panel.size.x, 0.2)
tween.parallel().tween_property(main_button, "position:x", 0, 0.2)
tween.tween_callback(func():
selection_panel.visible = false
click_blocker.visible = false
main_button.text = ""
visible = false
# Resume the game when the panel is closed
get_tree().paused = false
# Emit signal that panel was closed
closed.emit()
)
func _on_main_button_pressed() -> void:
if !selection_panel or !main_button or !click_blocker:
return
if !is_panel_visible:
show_panel()
else:
hide_panel()
func _create_filter_buttons():
if not filter_buttons or not builder:
return
# Clear existing buttons
for child in ground_options.get_children():
child.queue_free()
for child in building_options.get_children():
for child in filter_buttons.get_children():
child.queue_free()
# Create "All" button
var all_button = Button.new()
all_button.text = "All"
all_button.toggle_mode = true
all_button.button_pressed = true
all_button.flat = true
all_button.pressed.connect(_on_filter_button_pressed.bind("All"))
filter_buttons.add_child(all_button)
# Get unique structure types
var structure_types = {}
for structure in builder.get_structures():
if structure.type == Structure.StructureType.TERRAIN:
structure_types["Ground"] = true
else:
structure_types["Buildings"] = true
# Create buttons for each structure type
for type_name in structure_types.keys():
var button = Button.new()
button.text = type_name
button.toggle_mode = true
button.flat = true
button.pressed.connect(_on_filter_button_pressed.bind(type_name))
filter_buttons.add_child(button)
func _create_option_buttons():
# Clear existing buttons
if ground_options:
for child in ground_options.get_children():
child.queue_free()
if building_options:
for child in building_options.get_children():
child.queue_free()
# Get structures from builder
if not _builder or not _builder.structures:
if not builder:
print("ERROR: No builder reference in building selector")
return
var structures = builder.get_structures()
if not structures or structures.size() == 0:
print("WARNING: No structures available in builder")
return
# Create ground options (grass, pavement, etc.)
@ -66,7 +204,10 @@ func _create_option_buttons():
var building_structures = []
# Sort structures by type and apply filters
for structure in _builder.structures:
for structure in structures:
if not structure:
continue
# Apply search filter
if search_text != "" and not structure.title.to_lower().contains(search_text.to_lower()):
continue
@ -74,44 +215,57 @@ func _create_option_buttons():
# Apply type filter
if current_filter != "All":
match current_filter:
"Residential":
if structure.type != Structure.StructureType.RESIDENTIAL_BUILDING:
"Ground":
if structure.type != Structure.StructureType.TERRAIN:
continue
"Commercial":
if structure.type != Structure.StructureType.COMMERCIAL_BUILDING:
continue
"Industrial":
if structure.type != Structure.StructureType.INDUSTRIAL_BUILDING:
"Buildings":
if structure.type == Structure.StructureType.TERRAIN:
continue
# Add to appropriate list
if structure.type == Structure.StructureType.TERRAIN:
ground_structures.append(structure)
else:
building_structures.append(structure)
# Set up grid layout for ground options
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
if ground_options:
ground_options.columns = 4
ground_options.add_theme_constant_override("h_separation", 10)
ground_options.add_theme_constant_override("v_separation", 10)
# Create buttons for ground structures
for i in range(ground_structures.size()):
var button = _create_option_button(ground_structures[i], i)
ground_options.add_child(button)
if ground_options:
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
if building_options:
building_options.columns = 4
building_options.add_theme_constant_override("h_separation", 10)
building_options.add_theme_constant_override("v_separation", 10)
# Create buttons for building structures
for i in range(building_structures.size()):
var button = _create_option_button(building_structures[i], i + ground_structures.size())
building_options.add_child(button)
if building_options:
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
var ground_section = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/GroundSection
var building_section = $SelectionPanel/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/BuildingSection
if ground_section:
ground_section.visible = ground_structures.size() > 0
if building_section:
building_section.visible = building_structures.size() > 0
# Force update the layout
if ground_options:
ground_options.queue_redraw()
if building_options:
building_options.queue_redraw()
func _create_option_button(structure: Structure, index: int) -> Button:
var button = Button.new()
@ -199,33 +353,21 @@ func _create_option_button(structure: Structure, index: int) -> Button:
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):
func _on_search_text_changed(new_text: String) -> void:
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
func _on_filter_button_pressed(filter: String) -> void:
current_filter = filter
_create_option_buttons()
func _on_option_selected(index: int):
if not _builder:
if not builder:
print("ERROR: No builder reference in building selector")
return
if not _builder.structures or index < 0 or index >= _builder.structures.size():
var structures = builder.get_structures()
if not structures or index < 0 or index >= structures.size():
print("ERROR: Invalid structure index: ", index)
return
@ -233,8 +375,8 @@ func _on_option_selected(index: int):
_update_button_states()
# Update the builder's current selection
_builder.index = index
_builder.update_structure()
builder.index = index
builder.update_structure()
# Update the main button text
main_button.text = "Selected: " + _get_structure_name(index)
@ -244,18 +386,36 @@ func _on_option_selected(index: int):
func _update_button_states():
# Update all buttons to show which one is selected
var all_buttons = ground_options.get_children() + building_options.get_children()
if not ground_options or not building_options:
return
var all_buttons = []
# Add ground options buttons if they exist
for child in ground_options.get_children():
if child is Button:
all_buttons.append(child)
# Add building options buttons if they exist
for child in building_options.get_children():
if child is Button:
all_buttons.append(child)
# Update button states
for i in range(all_buttons.size()):
all_buttons[i].button_pressed = (i == current_selection)
if all_buttons[i] is Button:
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]
var structures = builder.get_structures()
if structures and index >= 0 and index < structures.size():
var structure = structures[index]
return structure.title
return "Unknown"
func _update_description_panel(index: int):
if not _builder or not _builder.structures or index < 0 or index >= _builder.structures.size():
var structures = builder.get_structures()
if not structures or index < 0 or index >= structures.size():
title_label.text = "No Building Selected"
description_label.text = "Select a building to view its details"
price_label.text = "Price: $0"
@ -263,7 +423,7 @@ func _update_description_panel(index: int):
power_label.text = "Power: 0 kW"
return
var structure = _builder.structures[index]
var structure = structures[index]
title_label.text = structure.title
description_label.text = structure.description
price_label.text = "Price: $" + str(structure.price)
@ -276,4 +436,48 @@ func _update_description_panel(index: int):
power_text += "-" + str(structure.kW_usage) + " kW"
else:
power_text += "0 kW"
power_label.text = power_text
power_label.text = power_text
func apply_resource_data(data: GenericText) -> void:
if data:
if title_label:
title_label.text = data.title
if description_label:
description_label.text = data.body_text
# Override _gui_input to ensure we're handling all input
func _gui_input(event: InputEvent) -> void:
if is_panel_visible:
# When panel is visible, accept all input to prevent it from reaching the game
get_viewport().set_input_as_handled()
# Override _input to catch all input events
func _input(event: InputEvent) -> void:
if is_panel_visible and (event is InputEventMouseButton or event is InputEventMouseMotion):
# When panel is visible, accept all mouse input to prevent it from reaching the game
get_viewport().set_input_as_handled()
# If it's a mouse button press, mark it as handled
if event is InputEventMouseButton:
event.pressed = false
# Override _unhandled_input to catch any remaining input events
func _unhandled_input(event: InputEvent) -> void:
if is_panel_visible:
get_viewport().set_input_as_handled()
if event is InputEventMouseButton:
event.pressed = false
# Override _get_global_rect to ensure the builder's UI check detects us
func _get_global_rect() -> Rect2:
if is_panel_visible:
return Rect2(Vector2.ZERO, get_viewport_rect().size)
return Rect2(Vector2.ZERO, Vector2.ZERO)
# Add a method to check if the mouse is over our panel
func is_mouse_over_building_selector() -> bool:
if is_panel_visible:
var mouse_pos = get_viewport().get_mouse_position()
var rect = get_global_rect()
return rect.has_point(mouse_pos)
return false

@ -16,6 +16,11 @@ var construction_sfx: AudioStreamPlayer
@export var intro_text_resource: GenericText
@export var outro_text_resource: GenericText
# Node references
@onready var building_selector = get_node_or_null("CanvasLayer/BuildingSelector")
@onready var resource_display = get_node_or_null("CanvasLayer/ResourceDisplay")
@onready var game_menu = get_node_or_null("CanvasLayer/GameMenu")
func _ready():
print("GameManager: Initializing...")
# Load data from a file.
@ -56,11 +61,12 @@ func _ready():
Engine.get_main_loop().set_meta("sound_manager", sound_manager)
# Reference to the controls panel and HUD
var controls_panel = $CanvasLayer/ControlsPanel
var hud = $CanvasLayer/HUD
var controls_panel = get_node_or_null("CanvasLayer/ControlsPanel")
var hud = get_node_or_null("CanvasLayer/HUD")
# Set up the HUD's reference to the panels
hud.controls_panel = controls_panel
if hud and controls_panel:
hud.controls_panel = controls_panel
# Show intro text if available
if generic_text_panel and intro_text_resource:
@ -94,6 +100,7 @@ func _ready():
var builder = get_node_or_null("/root/Main/Builder")
if builder:
builder.structure_placed.connect(_on_structure_placed)
print("GameManager: Connected to Builder signals")
# Connect to construction signals via deferred call to make sure everything is ready
call_deferred("_setup_construction_signals")
@ -102,9 +109,14 @@ func _ready():
call_deferred("_setup_sound_buses")
# Connect the building selector to the builder
var building_selector = $CanvasLayer/BuildingSelector
if building_selector:
building_selector.builder = $Builder
if builder:
building_selector.builder = builder
print("GameManager: Connected BuildingSelector to Builder")
else:
print("GameManager: Warning - Builder not found!")
else:
print("GameManager: Warning - BuildingSelector not found!")
# Connect builder's cash display to HUD
if builder and hud:
@ -122,6 +134,18 @@ func _ready():
economy_manager.population_changed.connect(hud_manager.update_population_count)
economy_manager.energy_balance_changed.connect(hud_manager.update_energy_balance)
# Initialize managers
_initialize_managers()
# Connect signals
_connect_signals()
# Initialize game state
_initialize_game_state()
# Start the game
start_game()
func _on_music_volume_changed(new_volume: float):
print("GameManager: Music volume changed to ", new_volume)
config.set_value("audio", "music_volume", new_volume)
@ -395,3 +419,23 @@ func _on_mission_manager_mission_started(mission: MissionData) -> void:
generic_text_panel.apply_resource_data(mission_text)
generic_text_panel.show_panel()
func _initialize_managers():
print("GameManager: Initializing managers")
# Initialize any required managers here
pass
func _connect_signals():
print("GameManager: Connecting signals")
# Connect any required signals here
pass
func _initialize_game_state():
print("GameManager: Initializing game state")
# Initialize game state here
pass
func start_game():
print("GameManager: Starting game")
# Start game logic here
pass

@ -46,11 +46,17 @@ func _ready():
# Setup mission select button
if mission_select_button:
mission_select_button.connect("pressed", _on_mission_select_button_pressed)
if not mission_select_button.pressed.is_connected(_on_mission_select_button_pressed):
mission_select_button.pressed.connect(_on_mission_select_button_pressed)
else:
push_error("Mission select button not found in HUD")
# Setup mission select menu
_setup_mission_select_menu()
# Wait a frame to ensure all nodes are ready
await get_tree().process_frame
# Update mission select visibility based on export variable
_update_mission_select_visibility()
@ -97,9 +103,15 @@ func _setup_mission_select_menu():
# Update mission select visibility based on export variable
func _update_mission_select_visibility():
var mission_select_item = $HBoxContainer/MissionSelectItem
if not is_inside_tree():
# If we're not in the tree yet, wait until we are
await ready
var mission_select_item = get_node_or_null("HBoxContainer/MissionSelectItem")
if mission_select_item:
mission_select_item.visible = show_mission_select
else:
push_warning("MissionSelectItem node not found in HUD")
# Handle mission select button press
func _on_mission_select_button_pressed():

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

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

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

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

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

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

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

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

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

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

@ -20,4 +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
@export var starting_structures: Array[String] = [] # Array of structure resource paths that are unlocked when this mission starts

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

File diff suppressed because it is too large Load Diff

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

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

@ -0,0 +1,5 @@
extends Resource
class_name PatternRules
@export var pattern_size: Vector2i = Vector2i(2, 2) # Size of the pattern grid
@export var rules: Array[PatternRule] # Array of pattern rules to check

@ -0,0 +1,83 @@
extends Node
# Process structure unlocking based on mission data
static func process_mission_structures(structures: Array[Structure], mission_data):
print("\n=== Processing Mission Structures ===")
print("Mission ID: ", mission_data.id)
# Print current unlock status
print("\nCurrent structure status:")
for i in range(structures.size()):
var structure = structures[i]
if structure.model:
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
# Don't lock all structures at start - only unlock new ones
var paths_to_unlock = []
# Only add starting structures - unlocked_items are handled when mission completes
if mission_data.starting_structures.size() > 0:
print("\nProcessing starting structures: ", mission_data.starting_structures)
paths_to_unlock.append_array(mission_data.starting_structures)
# Process all paths to unlock
print("\nAttempting to unlock ", paths_to_unlock.size(), " structures")
for path in paths_to_unlock:
unlock_structure_by_path(structures, path)
# Print final unlock status
print("\nFinal structure status:")
for i in range(structures.size()):
var structure = structures[i]
if structure.model:
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
print("=== Structure Processing Complete ===\n")
# Helper function to unlock a structure by its resource path
static func unlock_structure_by_path(structures: Array[Structure], path: String):
print("\nTrying to unlock structure with path: ", path)
# Print all structures and their current unlock status
print("\nCurrent structure status:")
for i in range(structures.size()):
var structure = structures[i]
if structure.model:
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
print("\nStarting structure matching...")
for structure in structures:
if not structure.model:
print("WARNING: Structure has no model!")
continue
var structure_path = structure.model.resource_path
print("\nChecking structure: ", structure_path)
# Try exact path match first
if structure_path == path:
print("Found exact path match, unlocking")
structure.unlocked = true
break
# Try converting between structures/ and models/ paths
if path.ends_with(".tres") and "structures/" in path:
var glb_path = path.replace("structures/", "models/").replace(".tres", ".glb")
if structure_path == glb_path:
print("Found converted path match (tres->glb), unlocking")
structure.unlocked = true
break
if path.ends_with(".glb") and "models/" in path:
var tres_path = path.replace("models/", "structures/").replace(".glb", ".tres")
if structure_path == tres_path:
print("Found converted path match (glb->tres), unlocking")
structure.unlocked = true
break
# Print final unlock status for all structures
print("\nFinal structure status after matching:")
for i in range(structures.size()):
var structure = structures[i]
if structure.model:
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")

@ -33,7 +33,6 @@ func setup(unlocked_structures):
# Debug info about structures
for i in range(unlocked_structures.size()):
if unlocked_structures[i].model:
print("Structure " + str(i) + ": " + unlocked_structures[i].model.resource_path)
if "title" in unlocked_structures[i]:
print(" Title: " + unlocked_structures[i].title)
if "description" in unlocked_structures[i]:
@ -163,7 +162,12 @@ func show_all_unlocked_structures():
var all_unlocked = []
# Get all unlocked structures from the builder
for structure in builder.structures:
var structures = builder.get_structures()
if not structures:
print("ERROR: No structures available")
return
for structure in structures:
if "unlocked" in structure and structure.unlocked:
all_unlocked.append(structure)

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