Add currency database, cleanup legacy system

This commit is contained in:
2026-03-14 17:20:22 +01:00
parent 6da962a53d
commit 7b58c36414
20 changed files with 353 additions and 222 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

10
currency/knowledge.tres Normal file
View File

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

10
currency/magic.tres Normal file
View File

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

View File

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

View File

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

View File

@@ -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 == &"":

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,7 @@ config/icon="res://icon.svg"
[autoload]
CurrencyDatabase="*res://currency/currency_database.gd"
GameState="*uid://d2j7tvlgxr2jp"
[input]