Merge branch 'jclemente/4_4_update' of https://github.com/STEMuli-Tx/stem-city into jclemente/4_4_update

# Conflicts:
#	scenes/main.tscn
#	scripts/javascript_global.gd.uid
pull/18/head
Wade 2025-05-03 15:43:14 +07:00
commit f82c182e71
10 changed files with 616 additions and 4 deletions

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

@ -1,4 +1,4 @@
[gd_scene load_steps=73 format=3 uid="uid://b6eb1v02n61vv"]
[gd_scene load_steps=74 format=3 uid="uid://vgwrcfy1qawf"]
[ext_resource type="Script" uid="uid://c37h6na3e4twn" path="res://scripts/builder.gd" id="1_jybm7"]
[ext_resource type="Environment" uid="uid://jbptgqvstei3" path="res://scenes/main-environment.tres" id="1_yndf3"]
@ -17,7 +17,7 @@
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="13_kf5aa"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://b6kf2nelnggk6" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://dnquivpg0ead" path="res://scripts/mission/learning_panel.gd" id="14_q2ymb"]
[ext_resource type="Resource" uid="uid://dqqe3iofnleup" path="res://structures/pavement-fountain.tres" id="14_t5ykj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
@ -34,7 +34,7 @@
[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="PackedScene" 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"]
@ -64,6 +64,7 @@
[ext_resource type="Resource" uid="uid://cpfr2xnjtpcog" path="res://mission/unit_1.07/resource_alloc_4.tres" id="50_6ke0d"]
[ext_resource type="Script" uid="uid://be2nkvjhpebhi" path="res://scripts/mission/mission_objective.gd" id="51_kf5aa"]
[ext_resource type="Resource" uid="uid://bsic030rpgh08" path="res://mission/unit_1.06/sustainable_dev_2b.tres" id="57_e755i"]
[ext_resource type="PackedScene" uid="uid://blfrsgh3cct2l" path="res://scenes/structure_menu.tscn" id="58_structure_menu"]
[sub_resource type="Resource" id="Resource_1gdbm"]
script = ExtResource("14_76jlq")
@ -259,8 +260,17 @@ visible = false
[node name="BuildingSelector" parent="CanvasLayer" instance=ExtResource("25_od4ux")]
visible = false
layout_direction = 2
offset_right = -277.0
[node name="StructureMenu" parent="CanvasLayer" instance=ExtResource("58_structure_menu")]
offset_left = 4.0
offset_top = 83.0
offset_right = 304.0
offset_bottom = 683.0
grow_horizontal = 1
grow_vertical = 1
[node name="MissionManager" type="Node" parent="." node_paths=PackedStringArray("mission_ui", "builder")]
script = ExtResource("10_oe3re")
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")])

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

@ -213,6 +213,10 @@ func _process(delta):
# Function to check if the mouse is over any UI elements
func is_mouse_over_ui() -> bool:
# Check if mouse is over the structure menu via HUD
if hud_manager and hud_manager.has_method("is_mouse_over_structure_menu") and hud_manager.is_mouse_over_structure_menu():
return true
# Get mouse position
var mouse_pos = get_viewport().get_mouse_position()

@ -24,6 +24,7 @@ var population_tooltip: Control
var electricity_tooltip: Control
var controls_panel: PanelContainer
var sound_panel: PanelContainer
var structure_menu: Control
@onready var _builder = get_node_or_null("/root/Main/Builder")
func _ready():
@ -43,6 +44,7 @@ func _ready():
# Get references to panels
controls_panel = get_node_or_null("/root/Main/CanvasLayer/ControlsPanel")
sound_panel = get_node_or_null("/root/Main/CanvasLayer/SoundPanel")
structure_menu = get_node_or_null("/root/Main/CanvasLayer/StructureMenu")
# Setup mission select button
if mission_select_button:
@ -54,6 +56,9 @@ func _ready():
# Setup mission select menu
_setup_mission_select_menu()
# Setup structure menu
_setup_structure_menu()
# Wait a frame to ensure all nodes are ready
await get_tree().process_frame
@ -101,6 +106,23 @@ func _setup_mission_select_menu():
# Make sure it's initially hidden
mission_select_menu.hide()
# Set up the structure menu
func _setup_structure_menu():
# Check if the structure menu already exists
structure_menu = get_node_or_null("/root/Main/CanvasLayer/StructureMenu")
# If not, instantiate and add it
if not structure_menu:
var structure_menu_scene = load("res://scenes/structure_menu.tscn")
if structure_menu_scene:
structure_menu = structure_menu_scene.instantiate()
var canvas_layer = get_node_or_null("/root/Main/CanvasLayer")
if canvas_layer:
canvas_layer.add_child(structure_menu)
# Set the builder reference
if _builder:
structure_menu.builder = _builder
# Update mission select visibility based on export variable
func _update_mission_select_visibility():
if not is_inside_tree():
@ -288,3 +310,8 @@ func _on_music_muted_changed(is_muted):
# Called when the sfx is muted
func _on_sfx_muted_changed(is_muted):
pass # Sound panel handles this through signals
func is_mouse_over_structure_menu() -> bool:
if structure_menu and structure_menu.has_method("is_mouse_over_menu"):
return structure_menu.is_mouse_over_menu()
return false

@ -1 +1 @@
uid://g0rf3gsjgld
uid://boq8j7e7veaup

@ -0,0 +1,314 @@
extends Control
@export var builder: Node3D
@export var base_menu_width: float = 300.0 # Base width for the menu
@export var item_width: float = 280.0 # Width of each item
@export var item_spacing: float = 20.0 # Spacing between items
@export var menu_speed: float = 0.3
@export var visible_menu_width: float = 600.0 # The visible width of the menu panel
@export var item_height: float = 180.0
@export var menu_vertical_offset: float = 40.0
var is_open: bool = false
var selected_index: int = -1
var menu_width: float = base_menu_width # Will be updated based on items
@onready var toggle_button = $ToggleButton
@onready var menu_panel = $MenuPanel
@onready var items_container = $MenuPanel/ScrollContainer/ItemsContainer
func _ready():
# Ensure we have a valid builder reference
if not builder:
builder = get_node_or_null("/root/Main/Builder")
if not builder:
push_error("StructureMenu: Builder node not found!")
return
# Initialize menu panel position and size
menu_panel.size.x = base_menu_width
menu_panel.position.x = -menu_panel.size.x # Start closed
# Vertically center the toggle button on the menu panel
toggle_button.anchor_top = 0.5
toggle_button.anchor_bottom = 0.5
toggle_button.offset_top = -toggle_button.size.y / 2
toggle_button.offset_bottom = toggle_button.size.y / 2
# Place toggle button at the left edge of the menu
toggle_button.position.x = 0
toggle_button.text = ""
# Connect signals
toggle_button.pressed.connect(_on_toggle_button_pressed)
# Connect to builder's structure update signal if it exists
if "structure_updated" in builder:
print("Connecting to builder's structure_updated signal") # Debug print
builder.structure_updated.connect(_on_builder_structure_updated)
# Wait a frame to ensure all nodes are ready
await get_tree().process_frame
_center_toggle_button()
# Populate the menu
populate_menu()
# Print debug info
print("StructureMenu: Builder found: ", builder != null)
print("StructureMenu: Structures array size: ", builder.structures.size() if builder and "structures" in builder else 0)
# Initialize selection if builder has a current index
if builder and "index" in builder:
selected_index = builder.index
update_selection_highlight()
func _center_toggle_button():
# Vertically center the toggle button on the menu panel
var menu_height = menu_panel.size.y
var button_height = toggle_button.size.y
toggle_button.position.y = (menu_height - button_height) / 2
# Horizontally: always at 0 when closed, at menu_panel.size.x when open
toggle_button.position.x = menu_panel.size.x if is_open else 0
func _on_toggle_button_pressed():
is_open = !is_open
var tween = create_tween()
tween.tween_property(menu_panel, "position:x", 0.0 if is_open else -menu_panel.size.x, menu_speed)
tween.parallel().tween_property(toggle_button, "position:x", menu_panel.size.x if is_open else 0.0, menu_speed)
toggle_button.text = "" if is_open else ""
_center_toggle_button()
func _on_builder_structure_updated(index):
print("Builder structure updated signal received. Index: ", index) # Debug print
# Update the selected index and highlight
selected_index = index
update_selection_highlight()
func populate_menu():
if not builder or not "structures" in builder:
push_error("StructureMenu: Builder or structures array not found!")
return
# Clear existing items
for child in items_container.get_children():
child.queue_free()
# Count unlocked structures
var unlocked_count = 0
for structure in builder.structures:
if "unlocked" in structure and structure.unlocked:
unlocked_count += 1
# Calculate menu width and height using the user's formula
var padding = item_spacing
menu_width = (unlocked_count * item_width) + ((unlocked_count + 1) * padding)
var menu_height = item_height + 2 * padding
# Center the menu panel horizontally and set its size using anchors and offsets
menu_panel.anchor_left = 0.5
menu_panel.anchor_right = 0.5
menu_panel.offset_left = -menu_width / 2
menu_panel.offset_right = menu_width / 2
menu_panel.anchor_top = 0.0
menu_panel.anchor_bottom = 0.0
menu_panel.offset_top = 0
menu_panel.offset_bottom = menu_height
$MenuPanel/ScrollContainer.size.x = menu_width
$MenuPanel/ScrollContainer.size.y = menu_height
$MenuPanel/ScrollContainer.position.y = 0
$MenuPanel/ScrollContainer.clip_contents = true
# If menu is closed, keep it offscreen
if not is_open:
menu_panel.position.x = -menu_width
_center_toggle_button()
# Create a MarginContainer for all-side padding
var margin = MarginContainer.new()
margin.add_theme_constant_override("margin_left", padding)
margin.add_theme_constant_override("margin_right", padding)
margin.add_theme_constant_override("margin_top", padding)
margin.add_theme_constant_override("margin_bottom", padding)
margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
margin.size_flags_vertical = Control.SIZE_EXPAND_FILL
items_container.add_child(margin)
margin.position.y = menu_vertical_offset
# Create a CenterContainer to center the row both horizontally and vertically
var center = CenterContainer.new()
center.size_flags_horizontal = Control.SIZE_EXPAND_FILL
center.size_flags_vertical = Control.SIZE_EXPAND_FILL
margin.add_child(center)
# Create a horizontal container for all items
var row = HBoxContainer.new()
row.add_theme_constant_override("separation", padding)
# DO NOT set row.size_flags_horizontal so it doesn't expand
center.add_child(row)
# Add all unlocked structures to the row
for i in range(builder.structures.size()):
var structure = builder.structures[i]
if "unlocked" in structure and structure.unlocked:
var item = create_structure_item(structure, i)
row.add_child(item)
# Update selection highlight after populating
update_selection_highlight()
func create_structure_item(structure, index):
var item = PanelContainer.new()
item.custom_minimum_size = Vector2(item_width, item_height)
item.size_flags_horizontal = 0 # Prevent horizontal expansion
item.add_theme_stylebox_override("panel", StyleBoxFlat.new())
var style = item.get_theme_stylebox("panel") as StyleBoxFlat
style.bg_color = Color(0.2, 0.2, 0.2, 0.8)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.3, 0.3, 0.3)
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
# Store the structure index in the item for reference
item.set_meta("structure_index", index)
# Main container for vertical layout
var vbox = VBoxContainer.new()
vbox.custom_minimum_size = Vector2(item_width, item_height)
vbox.size_flags_horizontal = 0 # Prevent horizontal expansion
vbox.add_theme_constant_override("separation", item_spacing)
item.add_child(vbox)
# Thumbnail container
var thumbnail_container = CenterContainer.new()
thumbnail_container.custom_minimum_size = Vector2(128, 128)
thumbnail_container.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
vbox.add_child(thumbnail_container)
# Thumbnail
var thumbnail = TextureRect.new()
thumbnail.custom_minimum_size = Vector2(128, 128)
thumbnail.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
thumbnail.size_flags_vertical = Control.SIZE_SHRINK_CENTER
thumbnail.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
# Try to load thumbnail
if "thumbnail" in structure and structure.thumbnail != null:
var texture = load(structure.thumbnail)
if texture != null:
# Scale the texture to 128x128
var image = texture.get_image()
image.resize(128, 128)
var scaled_texture = ImageTexture.create_from_image(image)
thumbnail.texture = scaled_texture
print("Structure ", index, " thumbnail loaded: ", structure.thumbnail)
print("Texture size: ", scaled_texture.get_size())
print("Thumbnail size: ", thumbnail.size)
print("Thumbnail container size: ", thumbnail_container.size)
thumbnail_container.add_child(thumbnail)
# Debug print after adding to container
print("After adding to container:")
print("Thumbnail size: ", thumbnail.size)
print("Thumbnail container size: ", thumbnail_container.size)
print("Thumbnail custom_minimum_size: ", thumbnail.custom_minimum_size)
print("Container custom_minimum_size: ", thumbnail_container.custom_minimum_size)
# Info container
var info = VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
info.add_theme_constant_override("separation", 5)
vbox.add_child(info)
# Structure name
var name_label = Label.new()
if "title" in structure and structure.title != null:
name_label.text = structure.title
else:
name_label.text = "Structure " + str(index)
name_label.add_theme_font_size_override("font_size", 18)
name_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0.2))
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
info.add_child(name_label)
# Structure cost
var cost_label = Label.new()
if "price" in structure:
cost_label.text = "Cost: $" + str(structure.price)
else:
cost_label.text = "Cost: $0"
cost_label.add_theme_color_override("font_color", Color(0.2, 0.8, 0.2))
cost_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
info.add_child(cost_label)
# Connect click handler
item.gui_input.connect(func(event): _on_item_gui_input(event, index))
# Add to container
items_container.add_child(item)
# Add separator if not last item
if index < builder.structures.size() - 1:
var sep = HSeparator.new()
items_container.add_child(sep)
func _on_item_gui_input(event, index):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
select_structure(index)
func select_structure(index):
if not builder or not "structures" in builder:
return
if index >= 0 and index < builder.structures.size():
print("Selecting structure index: ", index) # Debug print
# Update builder's selected structure
builder.index = index
if "update_structure" in builder:
builder.update_structure()
# Update our selected index
selected_index = index
# Update visual feedback
update_selection_highlight()
func update_selection_highlight():
print("Updating selection highlight. Selected index: ", selected_index) # Debug print
# Update selection highlight for all items
for i in range(items_container.get_child_count()):
var child = items_container.get_child(i)
if child is PanelContainer:
var style = child.get_theme_stylebox("panel") as StyleBoxFlat
var item_index = child.get_meta("structure_index")
if item_index == selected_index:
print("Highlighting item: ", item_index) # Debug print
# Selected item
style.border_color = Color(0.9, 0.9, 0.2) # Yellow border
style.bg_color = Color(0.3, 0.3, 0.3, 0.8) # Slightly lighter background
else:
# Unselected item
style.border_color = Color(0.3, 0.3, 0.3)
style.bg_color = Color(0.2, 0.2, 0.2, 0.8)
func get_structure_name(structure):
var file_name = structure.model.resource_path.get_file().get_basename()
var names = file_name.split("-")
var title = ""
for part in names:
if part.length() > 0:
title += part[0].to_upper() + part.substring(1) + " "
return title.strip_edges()
func is_mouse_over_menu() -> bool:
var mouse_pos = get_viewport().get_mouse_position()
return menu_panel.get_global_rect().has_point(mouse_pos) or toggle_button.get_global_rect().has_point(mouse_pos)

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

@ -0,0 +1,175 @@
extends Control
# Reference to the builder node
@export var builder: Node3D
# Menu properties
@export var menu_width: float = 300.0
@export var menu_speed: float = 0.3
# Menu state
var is_open: bool = false
var selected_index: int = -1
# References to UI elements
@onready var toggle_button = $ToggleButton
@onready var menu_panel = $MenuPanel
@onready var items_container = $MenuPanel/ScrollContainer/ItemsContainer
func _ready():
# Initialize menu state
menu_panel.position.x = -menu_width
menu_panel.size.x = menu_width
# Connect toggle button
toggle_button.pressed.connect(_on_toggle_button_pressed)
# Initial population
populate_menu()
# Connect to mission manager for updates
var mission_manager = get_node_or_null("/root/Main/MissionManager")
if mission_manager:
mission_manager.mission_started.connect(_on_mission_started)
mission_manager.mission_completed.connect(_on_mission_completed)
func _on_toggle_button_pressed():
is_open = !is_open
# Animate menu
var tween = create_tween()
tween.tween_property(menu_panel, "position:x", 0.0 if is_open else -menu_width, menu_speed)
# Update toggle button text
toggle_button.text = "" if is_open else ""
func populate_menu():
# Clear existing items
for child in items_container.get_children():
child.queue_free()
# Add unlocked structures
for i in range(builder.structures.size()):
var structure = builder.structures[i]
if "unlocked" in structure and structure.unlocked:
create_structure_item(structure, i)
func create_structure_item(structure, index):
var item = PanelContainer.new()
item.custom_minimum_size = Vector2(menu_width - 20, 100)
item.add_theme_stylebox_override("panel", StyleBoxFlat.new())
var style = item.get_theme_stylebox("panel") as StyleBoxFlat
style.bg_color = Color(0.2, 0.2, 0.2, 0.8)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.3, 0.3, 0.3)
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
var hbox = HBoxContainer.new()
hbox.custom_minimum_size = Vector2(menu_width - 20, 100)
item.add_child(hbox)
# Thumbnail
var thumbnail = TextureRect.new()
thumbnail.custom_minimum_size = Vector2(80, 80)
thumbnail.expand_mode = TextureRect.EXPAND_FILL_WIDTH
thumbnail.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
# Try to load thumbnail
if "thumbnail" in structure and structure.thumbnail:
var texture = load(structure.thumbnail)
if texture:
thumbnail.texture = texture
else:
# Try to get thumbnail from model path
var model_path = structure.model.resource_path
var colormap_path = model_path.get_basename() + "_colormap.png"
if ResourceLoader.exists(colormap_path):
thumbnail.texture = load(colormap_path)
hbox.add_child(thumbnail)
# Info container
var info = VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox.add_child(info)
# Structure name
var name_label = Label.new()
if "title" in structure and structure.title:
name_label.text = structure.title
else:
name_label.text = get_structure_name(structure)
name_label.add_theme_font_size_override("font_size", 18)
name_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0.2))
info.add_child(name_label)
# Structure cost
var cost_label = Label.new()
cost_label.text = "Cost: $" + str(structure.price)
cost_label.add_theme_color_override("font_color", Color(0.2, 0.8, 0.2))
info.add_child(cost_label)
# Structure description
var desc = Label.new()
if "description" in structure and structure.description:
desc.text = structure.description
else:
desc.text = "A structure for your city!"
desc.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
info.add_child(desc)
# Connect click handler
item.gui_input.connect(func(event): _on_item_gui_input(event, index))
# Add to container
items_container.add_child(item)
# Add separator if not last item
if index < builder.structures.size() - 1:
var sep = HSeparator.new()
items_container.add_child(sep)
func _on_item_gui_input(event, index):
if event is InputEventMouseButton and event.pressed and event.button_index == MouseButton.LEFT:
select_structure(index)
func select_structure(index):
if index >= 0 and index < builder.structures.size():
builder.index = index
builder.update_structure()
# Update selection highlight
for i in range(items_container.get_child_count()):
var child = items_container.get_child(i)
if child is PanelContainer:
var style = child.get_theme_stylebox("panel") as StyleBoxFlat
if i/2 == index: # Divide by 2 because of separators
style.border_color = Color(0.9, 0.9, 0.2)
else:
style.border_color = Color(0.3, 0.3, 0.3)
func get_structure_name(structure):
var file_name = structure.model.resource_path.get_file().get_basename()
var names = file_name.split("-")
var title = ""
for part in names:
if part.length() > 0:
title += part[0].to_upper() + part.substr(1) + " "
return title.strip_edges()
func _on_mission_started(mission):
# Update menu when a new mission starts
populate_menu()
func _on_mission_completed(mission):
# Update menu when a mission is completed (new structures may be unlocked)
populate_menu()

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