From 7b58c36414eea6cb3f20f66a45410a481d063a7b Mon Sep 17 00:00:00 2001 From: Michele Rossi Date: Sat, 14 Mar 2026 17:20:22 +0100 Subject: [PATCH] Add currency database, cleanup legacy system --- big_number_progress_bar.gd | 7 +- currency/currency_database.gd | 164 ++++++++++++++++++ currency/currency_database.gd.uid | 1 + currency/gems.tres | 10 -- currency/gold.tres | 10 -- currency/knowledge.tres | 10 ++ currency/magic.tres | 10 ++ currency_label.gd | 7 +- currency_tile.tscn | 3 +- game_state.gd | 161 +++-------------- .../generator_unlock_goals.json | 8 +- .../generator_unlock_system.gd | 12 +- generator-unlock-goals/goals_debug_ui.gd | 62 ++++++- generator/currency_generator.gd | 23 ++- generator/currency_generator.tscn | 25 +++ generator/primary_generator.tres | 4 +- generator/secondary_generator.tres | 4 +- generator_container.gd | 1 + generator_museum.tscn | 52 ++---- project.godot | 1 + 20 files changed, 353 insertions(+), 222 deletions(-) create mode 100644 currency/currency_database.gd create mode 100644 currency/currency_database.gd.uid delete mode 100644 currency/gems.tres delete mode 100644 currency/gold.tres create mode 100644 currency/knowledge.tres create mode 100644 currency/magic.tres create mode 100644 generator/currency_generator.tscn diff --git a/big_number_progress_bar.gd b/big_number_progress_bar.gd index ca85758..773a2e0 100644 --- a/big_number_progress_bar.gd +++ b/big_number_progress_bar.gd @@ -1,6 +1,6 @@ extends HBoxContainer -@export var currency: Resource = GameState.GOLD_CURRENCY +@export var currency: Resource @onready var _label_start = $LabelStart @onready var _label_end = $LabelEnd @@ -23,7 +23,10 @@ func set_limits(start: BigNumber, end: BigNumber) -> void: func _ready() -> void: if currency == null: - currency = GameState.GOLD_CURRENCY + currency = CurrencyDatabase.get_currency_resource(GameState.GOLD_CURRENCY_ID) + if currency == null: + push_warning("BigNumberProgressBar '%s' has no currency configured." % String(name)) + return _currency_id = GameState.get_currency_id(currency) GameState.currency_changed.connect(_on_currency_changed) diff --git a/currency/currency_database.gd b/currency/currency_database.gd new file mode 100644 index 0000000..a59d2d4 --- /dev/null +++ b/currency/currency_database.gd @@ -0,0 +1,164 @@ +extends Node + +#const GOLD_CURRENCY_ID: StringName = &"gold" +#const GEMS_CURRENCY_ID: StringName = &"gems" +#const LEGACY_CURRENCY_INDEX_TO_ID: Dictionary = { + #0: GOLD_CURRENCY_ID, + #1: GEMS_CURRENCY_ID, +#} + +const CURRENCY_DIRECTORY_PATH: String = "res://currency" +const CURRENCY_RESOURCE_EXTENSIONS: PackedStringArray = ["tres", "res"] + +var _currency_by_id: Dictionary = {} +var _catalog_initialized: bool = false + +func _ready() -> void: + _initialize_currency_catalog() + +func get_known_currencies() -> Array[Resource]: + _initialize_currency_catalog_if_needed() + + var result: Array[Resource] = [] + for raw_currency in _currency_by_id.values(): + var currency: Resource = raw_currency as Resource + if currency != null: + result.append(currency) + return result + +func get_known_currency_ids() -> Array[StringName]: + _initialize_currency_catalog_if_needed() + + var ids: Array[StringName] = [] + for raw_currency_id in _currency_by_id.keys(): + var currency_id: StringName = _normalize_currency_id(StringName(String(raw_currency_id))) + if currency_id == &"": + continue + ids.append(currency_id) + return ids + +func get_currency_id(currency: Resource) -> StringName: + if currency == null: + return &"" + + var raw_id: Variant = currency.get("id") + if raw_id is StringName: + return _normalize_currency_id(raw_id) + return _normalize_currency_id(StringName(String(raw_id))) + +func parse_currency_id(raw_currency: Variant) -> StringName: + #if raw_currency is int: + #return currency_id_from_legacy_index(raw_currency) + + var currency_text: String = String(raw_currency).to_lower().strip_edges() + if currency_text == "gem": + currency_text = "gems" + return _normalize_currency_id(StringName(currency_text)) + +#func currency_id_from_legacy_index(currency_index: int) -> StringName: + #return LEGACY_CURRENCY_INDEX_TO_ID.get(currency_index, &"") + +func is_known_currency_id(currency_id: StringName) -> bool: + _initialize_currency_catalog_if_needed() + + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return false + return _currency_by_id.has(normalized_currency_id) + +func get_currency_resource(currency_id: StringName) -> Resource: + _initialize_currency_catalog_if_needed() + + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return null + + var raw_currency: Variant = _currency_by_id.get(normalized_currency_id) + if raw_currency is Resource: + return raw_currency + return null + +func get_currency_name(currency_id: StringName) -> String: + var currency: Resource = get_currency_resource(currency_id) + if currency != null: + var display_name: String = String(currency.get("display_name")).strip_edges() + if not display_name.is_empty(): + return display_name + return _humanize_currency_id(currency_id) + +func get_currency_icon(currency_id: StringName) -> Texture2D: + var currency: Resource = get_currency_resource(currency_id) + if currency == null: + return null + + var raw_icon: Variant = currency.get("icon") + if raw_icon is Texture2D: + return raw_icon + return null + +func _initialize_currency_catalog_if_needed() -> void: + if _catalog_initialized: + return + _initialize_currency_catalog() + +func _initialize_currency_catalog() -> void: + _currency_by_id.clear() + _catalog_initialized = true + + var currency_resource_paths: PackedStringArray = _discover_currency_resource_paths() + for resource_path in currency_resource_paths: + var loaded_resource: Resource = load(resource_path) + if loaded_resource == null: + push_warning("Failed to load currency resource at '%s'." % resource_path) + continue + if not (loaded_resource is Currency): + continue + + var currency_id: StringName = get_currency_id(loaded_resource) + if currency_id == &"": + push_warning("Skipping currency with missing id: %s" % resource_path) + continue + if _currency_by_id.has(currency_id): + push_warning("Duplicate currency id '%s' found at %s. Keeping first occurrence." % [String(currency_id), resource_path]) + continue + + _currency_by_id[currency_id] = loaded_resource + + if _currency_by_id.is_empty(): + push_warning("No currency resources discovered under %s. Ensure currency .tres files exist and are included in export presets." % CURRENCY_DIRECTORY_PATH) + +func _discover_currency_resource_paths() -> PackedStringArray: + var result: PackedStringArray = [] + var directory: DirAccess = DirAccess.open(CURRENCY_DIRECTORY_PATH) + if directory == null: + push_warning("Unable to open currency directory: %s" % CURRENCY_DIRECTORY_PATH) + return result + + directory.list_dir_begin() + var entry_name: String = directory.get_next() + while not entry_name.is_empty(): + if directory.current_is_dir() or entry_name.begins_with("."): + entry_name = directory.get_next() + continue + + var extension: String = entry_name.get_extension().to_lower() + if CURRENCY_RESOURCE_EXTENSIONS.has(extension): + result.append("%s/%s" % [CURRENCY_DIRECTORY_PATH, entry_name]) + + entry_name = directory.get_next() + directory.list_dir_end() + + result.sort() + return result + +func _normalize_currency_id(currency_id: StringName) -> StringName: + var normalized: String = String(currency_id).to_lower().strip_edges() + if normalized.is_empty(): + return &"" + return StringName(normalized) + +func _humanize_currency_id(currency_id: StringName) -> String: + var normalized: String = String(_normalize_currency_id(currency_id)) + if normalized.is_empty(): + return "Unknown" + return normalized.replace("_", " ").capitalize() diff --git a/currency/currency_database.gd.uid b/currency/currency_database.gd.uid new file mode 100644 index 0000000..27c1cd2 --- /dev/null +++ b/currency/currency_database.gd.uid @@ -0,0 +1 @@ +uid://dmv83fnq26xao diff --git a/currency/gems.tres b/currency/gems.tres deleted file mode 100644 index ea52db2..0000000 --- a/currency/gems.tres +++ /dev/null @@ -1,10 +0,0 @@ -[gd_resource type="Resource" script_class="Currency" format=3] - -[ext_resource type="Script" path="res://currency/currency.gd" id="1_72iuq"] -[ext_resource type="Texture2D" path="res://icon.svg" id="2_x2h5x"] - -[resource] -script = ExtResource("1_72iuq") -id = &"gems" -display_name = "Gems" -icon = ExtResource("2_x2h5x") diff --git a/currency/gold.tres b/currency/gold.tres deleted file mode 100644 index cb10877..0000000 --- a/currency/gold.tres +++ /dev/null @@ -1,10 +0,0 @@ -[gd_resource type="Resource" script_class="Currency" format=3] - -[ext_resource type="Script" path="res://currency/currency.gd" id="1_x4uiu"] -[ext_resource type="Texture2D" path="res://icon.svg" id="2_52ar0"] - -[resource] -script = ExtResource("1_x4uiu") -id = &"gold" -display_name = "Gold" -icon = ExtResource("2_52ar0") diff --git a/currency/knowledge.tres b/currency/knowledge.tres new file mode 100644 index 0000000..60d7156 --- /dev/null +++ b/currency/knowledge.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://bnhqk8b31mm4e"] + +[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://currency/currency.gd" id="1_72iuq"] +[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="2_x2h5x"] + +[resource] +script = ExtResource("1_72iuq") +id = &"knowledge" +display_name = "Knowledge" +icon = ExtResource("2_x2h5x") diff --git a/currency/magic.tres b/currency/magic.tres new file mode 100644 index 0000000..0a27d86 --- /dev/null +++ b/currency/magic.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://brqaojindcxa5"] + +[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://currency/currency.gd" id="1_x4uiu"] +[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="2_52ar0"] + +[resource] +script = ExtResource("1_x4uiu") +id = &"magic" +display_name = "Magic" +icon = ExtResource("2_52ar0") diff --git a/currency_label.gd b/currency_label.gd index 8c83bb3..efe32fa 100644 --- a/currency_label.gd +++ b/currency_label.gd @@ -1,7 +1,7 @@ class_name CurrencyTile extends HBoxContainer -@export var currency: Resource = GameState.GOLD_CURRENCY +@export var currency: Resource @onready var _icon: TextureRect = $CurrencyIcon @onready var _label: Label = $Label @@ -12,7 +12,10 @@ var _currency_id: StringName = &"" func _ready() -> void: if currency == null: - currency = GameState.GOLD_CURRENCY + currency = CurrencyDatabase.get_currency_resource(GameState.GOLD_CURRENCY_ID) + if currency == null: + push_warning("CurrencyTile '%s' has no currency configured." % String(name)) + return _currency_id = GameState.get_currency_id(currency) _label.text = "%s:" % GameState.get_currency_name(_currency_id) diff --git a/currency_tile.tscn b/currency_tile.tscn index d1f0dbf..6cc8b54 100644 --- a/currency_tile.tscn +++ b/currency_tile.tscn @@ -7,8 +7,9 @@ theme_override_constants/separation = 12 script = ExtResource("1_0hv07") [node name="CurrencyIcon" type="TextureRect" parent="." unique_id=935299369] -layout_mode = 2 +visible = false custom_minimum_size = Vector2(20, 20) +layout_mode = 2 stretch_mode = 5 [node name="Label" type="Label" parent="." unique_id=89234840] diff --git a/game_state.gd b/game_state.gd index 6dcd353..616c4ef 100644 --- a/game_state.gd +++ b/game_state.gd @@ -7,54 +7,20 @@ extends Node signal currency_changed(currency_id: StringName, new_amount: BigNumber) signal generator_state_changed(generator_id: StringName, state: Dictionary) -const GOLD_CURRENCY: Resource = preload("res://currency/gold.tres") -const GEMS_CURRENCY: Resource = preload("res://currency/gems.tres") -const DEFAULT_CURRENCIES: Array[Resource] = [GOLD_CURRENCY, GEMS_CURRENCY] - -const GOLD_CURRENCY_ID: StringName = &"gold" -const GEMS_CURRENCY_ID: StringName = &"gems" -const LEGACY_CURRENCY_INDEX_TO_ID: Dictionary = { - 0: GOLD_CURRENCY_ID, - 1: GEMS_CURRENCY_ID, -} - # ========================================== # STATE VARIABLES # ========================================== var _current_currency: Dictionary = {} var _total_currency_acquired: Dictionary = {} -var _currency_by_id: Dictionary = {} - -var gold: BigNumber: - get: - return _get_current_currency_ref(GOLD_CURRENCY_ID) - set(value): - _set_current_currency(GOLD_CURRENCY_ID, value) - -var gems: BigNumber: - get: - return _get_current_currency_ref(GEMS_CURRENCY_ID) - set(value): - _set_current_currency(GEMS_CURRENCY_ID, value) - -var total_gold_acquired: BigNumber: - get: - return _get_total_currency_ref(GOLD_CURRENCY_ID) - set(value): - _set_total_currency(GOLD_CURRENCY_ID, value) - -var total_gems_acquired: BigNumber: - get: - return _get_total_currency_ref(GEMS_CURRENCY_ID) - set(value): - _set_total_currency(GEMS_CURRENCY_ID, value) var generator_states: Dictionary = {} var last_save_time: int = 0 # Unix timestamp +# ========================================== +# Save / Load +# ========================================== static var file_name: String = "user://idle_save.json" - const GENERATOR_OWNED_KEY: String = "owned" const GENERATOR_PURCHASED_COUNT_KEY: String = "purchased_count" const GENERATOR_UNLOCKED_KEY: String = "unlocked" @@ -64,86 +30,44 @@ const CURRENT_KEY: String = "current" const TOTAL_KEY: String = "total" func _ready() -> void: - _initialize_currency_catalog() _initialize_currency_maps() load_game() # ========================================== # STATE MODIFIERS # ========================================== -func add_gold(amount: BigNumber) -> void: - add_currency_by_id(GOLD_CURRENCY_ID, amount) - -func spend_gold(cost: BigNumber) -> bool: - return spend_currency_by_id(GOLD_CURRENCY_ID, cost) - -func add_gems(amount: BigNumber) -> void: - add_currency_by_id(GEMS_CURRENCY_ID, amount) - -func spend_gems(cost: BigNumber) -> bool: - return spend_currency_by_id(GEMS_CURRENCY_ID, cost) +#func add_gold(amount: BigNumber) -> void: + #add_currency_by_id(GOLD_CURRENCY_ID, amount) +# +#func spend_gold(cost: BigNumber) -> bool: + #return spend_currency_by_id(GOLD_CURRENCY_ID, cost) +# +#func add_gems(amount: BigNumber) -> void: + #add_currency_by_id(GEMS_CURRENCY_ID, amount) +# +#func spend_gems(cost: BigNumber) -> bool: + #return spend_currency_by_id(GEMS_CURRENCY_ID, cost) func get_known_currencies() -> Array[Resource]: - var result: Array[Resource] = [] - for raw_currency in _currency_by_id.values(): - var currency: Resource = raw_currency as Resource - if currency != null: - result.append(currency) - return result + return CurrencyDatabase.get_known_currencies() func get_currency_id(currency: Resource) -> StringName: - if currency == null: - return &"" - - var raw_id: Variant = currency.get("id") - if raw_id is StringName: - return _normalize_currency_id(raw_id) - return _normalize_currency_id(StringName(String(raw_id))) + return CurrencyDatabase.get_currency_id(currency) func parse_currency_id(raw_currency: Variant) -> StringName: - if raw_currency is int: - return currency_id_from_legacy_index(raw_currency) - - var currency_text: String = String(raw_currency).to_lower().strip_edges() - if currency_text == "gem": - currency_text = "gems" - return _normalize_currency_id(StringName(currency_text)) - -func currency_id_from_legacy_index(currency_index: int) -> StringName: - return LEGACY_CURRENCY_INDEX_TO_ID.get(currency_index, &"") + return CurrencyDatabase.parse_currency_id(raw_currency) func is_known_currency_id(currency_id: StringName) -> bool: - var normalized_currency_id: StringName = _normalize_currency_id(currency_id) - if normalized_currency_id == &"": - return false - return _currency_by_id.has(normalized_currency_id) + return CurrencyDatabase.is_known_currency_id(currency_id) func get_currency_resource(currency_id: StringName) -> Resource: - var normalized_currency_id: StringName = _normalize_currency_id(currency_id) - if normalized_currency_id == &"": - return null - - var raw_currency: Variant = _currency_by_id.get(normalized_currency_id) - if raw_currency is Resource: - return raw_currency - return null + return CurrencyDatabase.get_currency_resource(currency_id) func get_currency_name(currency_id: StringName) -> String: - var currency: Resource = get_currency_resource(currency_id) - if currency != null: - var display_name: String = String(currency.get("display_name")).strip_edges() - if not display_name.is_empty(): - return display_name - return _humanize_currency_id(currency_id) + return CurrencyDatabase.get_currency_name(currency_id) func get_currency_icon(currency_id: StringName) -> Texture2D: - var currency: Resource = get_currency_resource(currency_id) - if currency == null: - return null - var raw_icon: Variant = currency.get("icon") - if raw_icon is Texture2D: - return raw_icon - return null + return CurrencyDatabase.get_currency_icon(currency_id) func add_currency(currency: Resource, amount: BigNumber) -> void: var currency_id: StringName = get_currency_id(currency) @@ -274,11 +198,6 @@ func set_generator_available(generator_id: StringName, value: bool) -> void: func save_game() -> void: var save_data = { CURRENCIES_KEY: _serialize_currency_balances(), - # Keep legacy fields for compatibility with older builds. - "gold": gold.serialize(), - "gems": gems.serialize(), - "total_gold_acquired": total_gold_acquired.serialize(), - "total_gems_acquired": total_gems_acquired.serialize(), "generator_states": _serialize_generator_states(), "last_save_time": Time.get_unix_time_from_system() } @@ -298,21 +217,10 @@ func load_game() -> void: var parsed_data: Dictionary = parsed if parsed_data.has(CURRENCIES_KEY): _load_currency_balances(parsed_data.get(CURRENCIES_KEY, {})) - else: - _load_legacy_currency_balances(parsed_data) generator_states = _deserialize_generator_states(parsed_data.get("generator_states", {})) last_save_time = parsed_data.get("last_save_time", Time.get_unix_time_from_system()) -func _load_legacy_currency_balances(parsed_data: Dictionary) -> void: - var loaded_gold: BigNumber = BigNumber.deserialize(parsed_data.get("gold", {})) - var loaded_gems: BigNumber = BigNumber.deserialize(parsed_data.get("gems", {})) - - _set_current_currency(GOLD_CURRENCY_ID, loaded_gold) - _set_current_currency(GEMS_CURRENCY_ID, loaded_gems) - _set_total_currency(GOLD_CURRENCY_ID, _deserialize_total_currency(parsed_data.get("total_gold_acquired", null), loaded_gold)) - _set_total_currency(GEMS_CURRENCY_ID, _deserialize_total_currency(parsed_data.get("total_gems_acquired", null), loaded_gems)) - func _load_currency_balances(raw_currencies: Variant) -> void: if not (raw_currencies is Dictionary): return @@ -375,31 +283,16 @@ func _collect_currency_ids_for_save() -> Array[StringName]: continue ids.append(normalized_currency_id) - for raw_id in _currency_by_id.keys(): - var normalized_currency_id: StringName = _normalize_currency_id(StringName(String(raw_id))) + for known_currency_id in CurrencyDatabase.get_known_currency_ids(): + var normalized_currency_id: StringName = _normalize_currency_id(known_currency_id) if normalized_currency_id == &"" or ids.has(normalized_currency_id): continue ids.append(normalized_currency_id) return ids -func _initialize_currency_catalog() -> void: - _currency_by_id.clear() - - for currency in DEFAULT_CURRENCIES: - if currency == null: - continue - - var currency_id: StringName = get_currency_id(currency) - if currency_id == &"": - push_warning("Skipping currency with missing id in DEFAULT_CURRENCIES.") - continue - - _currency_by_id[currency_id] = currency - func _initialize_currency_maps() -> void: - for currency in DEFAULT_CURRENCIES: - var currency_id: StringName = get_currency_id(currency) + for currency_id in CurrencyDatabase.get_known_currency_ids(): if currency_id == &"": continue _set_current_currency(currency_id, BigNumber.from_float(0.0)) @@ -411,12 +304,6 @@ func _normalize_currency_id(currency_id: StringName) -> StringName: return &"" return StringName(normalized) -func _humanize_currency_id(currency_id: StringName) -> String: - var normalized: String = String(_normalize_currency_id(currency_id)) - if normalized.is_empty(): - return "Unknown" - return normalized.replace("_", " ").capitalize() - func _get_current_currency_ref(currency_id: StringName) -> BigNumber: var normalized_currency_id: StringName = _normalize_currency_id(currency_id) if normalized_currency_id == &"": diff --git a/generator-unlock-goals/generator_unlock_goals.json b/generator-unlock-goals/generator_unlock_goals.json index 60d8895..812daa9 100644 --- a/generator-unlock-goals/generator_unlock_goals.json +++ b/generator-unlock-goals/generator_unlock_goals.json @@ -3,20 +3,20 @@ "goals": [ { "id": "first_goal", - "target_generator_id": "Gems", + "target_generator_id": "Knowledge", "requirements": [ { - "currency": "gold", + "currency": "magic", "amount": { "m": 30, "e": 0 } } ] }, { "id": "second_goal", - "target_generator_id": "Gems", + "target_generator_id": "Wood", "requirements": [ { - "currency": "gold", + "currency": "magic", "amount": { "m": 1.3, "e": 3 } } ] diff --git a/generator-unlock-goals/generator_unlock_system.gd b/generator-unlock-goals/generator_unlock_system.gd index 1d417c1..6618628 100644 --- a/generator-unlock-goals/generator_unlock_system.gd +++ b/generator-unlock-goals/generator_unlock_system.gd @@ -29,11 +29,19 @@ func _ready() -> void: _collect_known_generator_ids() _load_goals() GameState.currency_changed.connect(_on_currency_changed) + GameState.generator_state_changed.connect(_on_generator_state_changed) _evaluate_all_goals() + _evaluate_all_goals.call_deferred() func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void: _evaluate_all_goals() +func _on_generator_state_changed(generator_id: StringName, _state: Dictionary) -> void: + var generator_key: String = String(generator_id) + if not generator_key.is_empty(): + _known_generator_ids[generator_key] = true + _evaluate_all_goals() + func _evaluate_all_goals() -> void: if _goals.is_empty(): return @@ -185,7 +193,9 @@ func _get_total_currency_amount(currency_id: StringName) -> BigNumber: func _collect_known_generator_ids() -> void: _known_generator_ids.clear() - var generator_nodes: Array[Node] = find_children("*", "CurrencyGenerator", true, false) + var scene_root: Node = get_tree().current_scene + var search_root: Node = scene_root if scene_root != null else self + var generator_nodes: Array[Node] = search_root.find_children("*", "CurrencyGenerator", true, false) for generator_node in generator_nodes: var generator: CurrencyGenerator = generator_node as CurrencyGenerator if generator == null: diff --git a/generator-unlock-goals/goals_debug_ui.gd b/generator-unlock-goals/goals_debug_ui.gd index c6ba047..6dbe624 100644 --- a/generator-unlock-goals/goals_debug_ui.gd +++ b/generator-unlock-goals/goals_debug_ui.gd @@ -37,9 +37,11 @@ const GOALS_FILE_PATH: String = "res://generator-unlock-goals/generator_unlock_g var _goals: Array[GoalDefinition] = [] var _rows_by_goal_id: Dictionary = {} -var _completed_goal_ids: Dictionary = {} +var _known_generator_ids: Dictionary = {} +var _warned_unknown_target_ids: Dictionary = {} func _ready() -> void: + _collect_known_generator_ids() _load_goals() _build_goal_rows() GameState.currency_changed.connect(_on_currency_changed) @@ -50,7 +52,10 @@ func _ready() -> void: func _on_currency_changed(_currency_id: StringName, _new_amount: BigNumber) -> void: _refresh_ui() -func _on_generator_state_changed(_generator_id: StringName, _state: Dictionary) -> void: +func _on_generator_state_changed(generator_id: StringName, _state: Dictionary) -> void: + var generator_key: String = String(generator_id) + if not generator_key.is_empty(): + _known_generator_ids[generator_key] = true _refresh_ui() func _load_goals() -> void: @@ -183,7 +188,7 @@ func _refresh_ui() -> void: continue _refresh_goal_row(row) - if _is_goal_completed(goal.id): + if _is_goal_completed(goal): unlocked += 1 elif _is_goal_met(goal): ready_to_unlock += 1 @@ -192,9 +197,10 @@ func _refresh_ui() -> void: func _refresh_goal_row(row: GoalRow) -> void: var goal: GoalDefinition = row.goal - var already_unlocked: bool = _is_goal_completed(goal.id) + var already_unlocked: bool = _is_goal_completed(goal) + var can_resolve_target: bool = _can_resolve_target(goal.target_generator_id) var met: bool = _is_goal_met(goal) - var can_unlock: bool = met and not already_unlocked + var can_unlock: bool = can_resolve_target and met and not already_unlocked row.requirements_label.text = _get_requirements_text(goal) row.button.disabled = not can_unlock @@ -204,6 +210,10 @@ func _refresh_goal_row(row: GoalRow) -> void: row.status_label.text = "Unlocked" return + if not can_resolve_target: + row.status_label.text = "Missing" + return + if met: row.status_label.text = "Ready" return @@ -217,8 +227,11 @@ func _is_goal_met(goal: GoalDefinition) -> bool: return false return true -func _is_goal_completed(goal_id: StringName) -> bool: - return _completed_goal_ids.get(String(goal_id), false) +func _is_goal_completed(goal: GoalDefinition) -> bool: + return ( + GameState.is_generator_unlocked(goal.target_generator_id) + and GameState.is_generator_available(goal.target_generator_id) + ) func _on_unlock_pressed(goal_id: StringName) -> void: var row: GoalRow = _rows_by_goal_id.get(String(goal_id)) @@ -226,17 +239,48 @@ func _on_unlock_pressed(goal_id: StringName) -> void: return var goal: GoalDefinition = row.goal - if _is_goal_completed(goal.id): + if _is_goal_completed(goal): + return + if not _can_resolve_target(goal.target_generator_id): return if not _is_goal_met(goal): return - _completed_goal_ids[String(goal.id)] = true GameState.set_generator_unlocked(goal.target_generator_id, true) GameState.set_generator_available(goal.target_generator_id, true) print("[GoalsDebugUI] Unlocked goal '%s' -> %s" % [String(goal.id), String(goal.target_generator_id)]) _refresh_ui() +func _can_resolve_target(target_generator_id: StringName) -> bool: + var target_key: String = String(target_generator_id) + if target_key.is_empty(): + return false + + if _known_generator_ids.has(target_key) or GameState.generator_states.has(target_key): + return true + + if not _warned_unknown_target_ids.has(target_key): + _warned_unknown_target_ids[target_key] = true + push_warning("Goals debug UI target generator not found in scene/state: '%s'" % target_key) + return false + +func _collect_known_generator_ids() -> void: + _known_generator_ids.clear() + + var scene_root: Node = get_tree().current_scene + var search_root: Node = scene_root if scene_root != null else self + var generator_nodes: Array[Node] = search_root.find_children("*", "CurrencyGenerator", true, false) + for generator_node in generator_nodes: + var generator: CurrencyGenerator = generator_node as CurrencyGenerator + if generator == null: + continue + + var generator_key: String = String(generator.get_generator_id()) + if generator_key.is_empty(): + continue + + _known_generator_ids[generator_key] = true + func _get_requirements_text(goal: GoalDefinition) -> String: var parts: Array[String] = [] for requirement in goal.requirements: diff --git a/generator/currency_generator.gd b/generator/currency_generator.gd index 6020256..ce0d6b2 100644 --- a/generator/currency_generator.gd +++ b/generator/currency_generator.gd @@ -1,5 +1,5 @@ class_name CurrencyGenerator -extends Node +extends Node2D ## Handles generator economy behavior: ## - automatic production cycles @@ -18,7 +18,7 @@ signal production_tick(amount: BigNumber, cycle_count: int, total_owned: int) const HUGE_COST_EXPONENT: int = 1000000 ## Currency target updated by this generator. -@export var currency: Resource = GameState.GOLD_CURRENCY +@export var currency: Resource ## Data resource containing balancing values and defaults (required). @export var data: CurrencyGeneratorData ## Enables per-cycle automatic production when true. @@ -30,6 +30,8 @@ const HUGE_COST_EXPONENT: int = 1000000 ## Extra multiplier applied to automatic production output. @export var run_multiplier: float = 1.0 + + ## True while the pointer is inside the generator Area2D. var _mouse_entered: bool = false ## Time left until next click/hover grant is allowed. @@ -82,14 +84,14 @@ var _is_registered: bool = false ## Resolves and registers this generator state before gameplay updates. func _ready() -> void: + assert(currency != null, "Currency cannot be null") + assert(data != null, "Data cannot be null") + _generator_id = _resolve_generator_id() _ensure_registered() cycle_progress_seconds = 0.0 - if currency == null: - push_warning("CurrencyGenerator '%s' has no currency set; defaulting to Gold." % String(name)) - currency = GameState.GOLD_CURRENCY - if data == null: - push_error("CurrencyGenerator '%s' is missing CurrencyGeneratorData." % String(name)) + + GameState.generator_state_changed.connect(_on_generated_state_changed) ## Updates click cooldown/click grants and runs automatic production cycles. func _process(delta: float) -> void: @@ -342,3 +344,10 @@ func _on_area_2d_mouse_entered() -> void: ## Area2D callback: pointer exited generator interaction region. func _on_area_2d_mouse_exited() -> void: _mouse_entered = false + +func _on_generated_state_changed(generator_id: StringName, state: Dictionary) -> void: + if generator_id == _generator_id: + print(_generator_id) + print(state) + visible = true + diff --git a/generator/currency_generator.tscn b/generator/currency_generator.tscn new file mode 100644 index 0000000..279edb3 --- /dev/null +++ b/generator/currency_generator.tscn @@ -0,0 +1,25 @@ +[gd_scene format=3 uid="uid://jeoiinukrrsp"] + +[ext_resource type="Script" uid="uid://dtbxopw6ulhl8" path="res://generator/currency_generator.gd" id="1_4n4ca"] +[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://currency/magic.tres" id="2_5tmvy"] +[ext_resource type="Resource" uid="uid://co0mcc2kvcpo5" path="res://generator/primary_generator.tres" id="3_wx13b"] +[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="4_ruf1h"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_y5m1q"] +size = Vector2(128, 128) + +[node name="CurrencyGenerator" type="Node2D" unique_id=967969064] +script = ExtResource("1_4n4ca") +currency = ExtResource("2_5tmvy") +data = ExtResource("3_wx13b") + +[node name="Sprite2D" type="Sprite2D" parent="." unique_id=807584513] +texture = ExtResource("4_ruf1h") + +[node name="Area2D" type="Area2D" parent="." unique_id=707349247] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D" unique_id=1572620025] +shape = SubResource("RectangleShape2D_y5m1q") + +[connection signal="mouse_entered" from="Area2D" to="." method="_on_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Area2D" to="." method="_on_area_2d_mouse_exited"] diff --git a/generator/primary_generator.tres b/generator/primary_generator.tres index e96e49b..65cab5d 100644 --- a/generator/primary_generator.tres +++ b/generator/primary_generator.tres @@ -4,8 +4,8 @@ [resource] script = ExtResource("1_c6y77") -id = &"Gold" -name = "Gold Mine" +id = &"Magic" +name = "Magic Orb" initial_time = 0.6 initial_productivity = 1.67 metadata/_custom_type_script = "uid://b00tqsuhxdy0d" diff --git a/generator/secondary_generator.tres b/generator/secondary_generator.tres index b2da738..f31f396 100644 --- a/generator/secondary_generator.tres +++ b/generator/secondary_generator.tres @@ -4,8 +4,8 @@ [resource] script = ExtResource("1_s3g28") -id = &"Gems" -name = "Gems Mine" +id = &"Knowledge" +name = "Library" starts_unlocked = false starts_available = false initial_cost = 60.0 diff --git a/generator_container.gd b/generator_container.gd index 83d6f3c..9113e07 100644 --- a/generator_container.gd +++ b/generator_container.gd @@ -42,6 +42,7 @@ func _on_generator_updated(_a = null, _b = null, _c = null, _d = null) -> void: func _on_generator_state_changed(generator_id: StringName, _state: Dictionary) -> void: if generator_id == _generator.get_generator_id(): + visible = true _refresh_generator_ui() func _refresh_generator_ui() -> void: diff --git a/generator_museum.tscn b/generator_museum.tscn index 7f58d88..a504e27 100644 --- a/generator_museum.tscn +++ b/generator_museum.tscn @@ -1,35 +1,21 @@ [gd_scene format=3 uid="uid://cfryecxmcg8hw"] -[ext_resource type="Script" uid="uid://dtbxopw6ulhl8" path="res://generator/currency_generator.gd" id="1_pl17p"] -[ext_resource type="Resource" uid="uid://co0mcc2kvcpo5" path="res://generator/primary_generator.tres" id="2_x5lt1"] +[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://currency/magic.tres" id="2_bj0ey"] [ext_resource type="Resource" uid="uid://04pmc034qupd" path="res://generator/secondary_generator.tres" id="3_dc82g"] -[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="3_mhw8n"] [ext_resource type="PackedScene" uid="uid://ckos7f22pnmyh" path="res://generator_container.tscn" id="3_pw2a0"] [ext_resource type="PackedScene" uid="uid://btkxru2gdjsgc" path="res://currency_tile.tscn" id="3_x5lt1"] [ext_resource type="PackedScene" path="res://generator-unlock-goals/goals_debug_ui.tscn" id="4_63gjq"] -[ext_resource type="Resource" path="res://currency/gems.tres" id="6_gemsc"] - -[sub_resource type="RectangleShape2D" id="RectangleShape2D_y5m1q"] -size = Vector2(128, 128) +[ext_resource type="Resource" uid="uid://bnhqk8b31mm4e" path="res://currency/knowledge.tres" id="6_gemsc"] +[ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://generator/currency_generator.tscn" id="6_mhw8n"] [node name="GeneratorMuseum" type="Node2D" unique_id=1219373683] -[node name="GoldGenerator" type="Node2D" parent="." unique_id=964187690] -position = Vector2(566, 455) -script = ExtResource("1_pl17p") -data = ExtResource("2_x5lt1") -grants_click_while_hovering = true +[node name="MagicGenerator" parent="." unique_id=967969064 instance=ExtResource("6_mhw8n")] +position = Vector2(580, 114) -[node name="Sprite2D" type="Sprite2D" parent="GoldGenerator" unique_id=381586216] -texture = ExtResource("3_mhw8n") - -[node name="Area2D" type="Area2D" parent="GoldGenerator" unique_id=2084873356] - -[node name="CollisionShape2D" type="CollisionShape2D" parent="GoldGenerator/Area2D" unique_id=726858258] -shape = SubResource("RectangleShape2D_y5m1q") - -[node name="GemsGenerator" type="Node2D" parent="." unique_id=931294956] -script = ExtResource("1_pl17p") +[node name="KnowledgeGenerator" parent="." unique_id=2139088546 instance=ExtResource("6_mhw8n")] +visible = false +position = Vector2(761, 342) currency = ExtResource("6_gemsc") data = ExtResource("3_dc82g") @@ -43,6 +29,7 @@ offset_bottom = 40.0 layout_mode = 0 offset_right = 160.0 offset_bottom = 31.0 +currency = ExtResource("2_bj0ey") [node name="GemsCurrencyTile" parent="UI" unique_id=1977342362 instance=ExtResource("3_x5lt1")] layout_mode = 0 @@ -51,31 +38,26 @@ offset_right = 160.0 offset_bottom = 59.0 currency = ExtResource("6_gemsc") -[node name="GoldGeneratorContainer" parent="UI" unique_id=1129190041 node_paths=PackedStringArray("_generator") instance=ExtResource("3_pw2a0")] -visible = false +[node name="MagicGeneratorContainer" parent="UI" unique_id=1129190041 node_paths=PackedStringArray("_generator") instance=ExtResource("3_pw2a0")] layout_mode = 1 offset_left = -19.0 offset_top = 53.0 offset_right = 245.0 offset_bottom = 218.0 -_generator = NodePath("../../GoldGenerator") +_generator = NodePath("../../MagicGenerator") -[node name="GemsGeneratorContainer" parent="UI" unique_id=1999544736 node_paths=PackedStringArray("_generator") instance=ExtResource("3_pw2a0")] +[node name="KnowledgeGeneratorContainer" parent="UI" unique_id=1999544736 node_paths=PackedStringArray("_generator") instance=ExtResource("3_pw2a0")] visible = false layout_mode = 1 offset_left = -17.0 offset_top = 215.0 offset_right = 247.0 offset_bottom = 380.0 -_generator = NodePath("../../GemsGenerator") +_generator = NodePath("../../KnowledgeGenerator") [node name="GoalsDebugUI" parent="UI" unique_id=4710697 instance=ExtResource("4_63gjq")] -visible = false layout_mode = 1 -offset_left = 577.0 -offset_top = 3.0 -offset_right = 1150.0 -offset_bottom = 262.0 - -[connection signal="mouse_entered" from="GoldGenerator/Area2D" to="GoldGenerator" method="_on_area_2d_mouse_entered"] -[connection signal="mouse_exited" from="GoldGenerator/Area2D" to="GoldGenerator" method="_on_area_2d_mouse_exited"] +offset_left = 846.0 +offset_top = 390.0 +offset_right = 1153.0 +offset_bottom = 649.0 diff --git a/project.godot b/project.godot index 6427075..f043559 100644 --- a/project.godot +++ b/project.godot @@ -17,6 +17,7 @@ config/icon="res://icon.svg" [autoload] +CurrencyDatabase="*res://currency/currency_database.gd" GameState="*uid://d2j7tvlgxr2jp" [input]