diff --git a/big_number_progress_bar.gd b/big_number_progress_bar.gd index bffeca9..ca85758 100644 --- a/big_number_progress_bar.gd +++ b/big_number_progress_bar.gd @@ -1,6 +1,6 @@ extends HBoxContainer -@export var currency: GameState.CurrencyType +@export var currency: Resource = GameState.GOLD_CURRENCY @onready var _label_start = $LabelStart @onready var _label_end = $LabelEnd @@ -9,6 +9,7 @@ extends HBoxContainer var _start: BigNumber var _end: BigNumber var _current: BigNumber +var _currency_id: StringName = &"" func set_limits(start: BigNumber, end: BigNumber) -> void: _start = start @@ -21,17 +22,17 @@ func set_limits(start: BigNumber, end: BigNumber) -> void: _progress_bar.max_value = _end.get_ratio(_start) func _ready() -> void: + if currency == null: + currency = GameState.GOLD_CURRENCY + _currency_id = GameState.get_currency_id(currency) + GameState.currency_changed.connect(_on_currency_changed) - match currency: - GameState.CurrencyType.gold: - _current = GameState.gold - GameState.CurrencyType.gems: - _current = GameState.gems + _current = GameState.get_currency_amount_by_id(_currency_id) set_limits(_current, _current.add(BigNumber.new(1, 1))) -func _on_currency_changed(changed_currency: GameState.CurrencyType, amount: BigNumber) -> void: - if changed_currency != currency: +func _on_currency_changed(changed_currency_id: StringName, amount: BigNumber) -> void: + if changed_currency_id != _currency_id: return _current = amount _progress_bar.value = amount.get_ratio(_end) diff --git a/currency/currency.gd b/currency/currency.gd new file mode 100644 index 0000000..31f81e8 --- /dev/null +++ b/currency/currency.gd @@ -0,0 +1,6 @@ +class_name Currency +extends Resource + +@export var id: StringName = &"" +@export var display_name: String = "" +@export var icon: Texture2D diff --git a/currency/currency.gd.uid b/currency/currency.gd.uid new file mode 100644 index 0000000..40ed822 --- /dev/null +++ b/currency/currency.gd.uid @@ -0,0 +1 @@ +uid://dtgqjf3bl7pm8 diff --git a/currency/gems.tres b/currency/gems.tres new file mode 100644 index 0000000..ea52db2 --- /dev/null +++ b/currency/gems.tres @@ -0,0 +1,10 @@ +[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 new file mode 100644 index 0000000..cb10877 --- /dev/null +++ b/currency/gold.tres @@ -0,0 +1,10 @@ +[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_label.gd b/currency_label.gd index 8b89332..8c83bb3 100644 --- a/currency_label.gd +++ b/currency_label.gd @@ -1,26 +1,33 @@ class_name CurrencyTile extends HBoxContainer -@export var currency: GameState.CurrencyType -@export var data: CurrencyGeneratorData +@export var currency: Resource = GameState.GOLD_CURRENCY +@onready var _icon: TextureRect = $CurrencyIcon @onready var _label: Label = $Label @onready var _currency_label: Label = $CurrencyValueLabel @onready var _debug_button: Button = $DebugIncomeButton -func _ready() -> void: - _label.text = data.name - GameState.currency_changed.connect(_on_currency_changed) - match currency: - GameState.CurrencyType.gold: - _on_currency_changed(currency, GameState.gold) - GameState.CurrencyType.gems: - _on_currency_changed(currency, GameState.gems) +var _currency_id: StringName = &"" -func _on_currency_changed(changed_currency: GameState.CurrencyType, amount: BigNumber) -> void: - if changed_currency != currency: +func _ready() -> void: + if currency == null: + currency = GameState.GOLD_CURRENCY + + _currency_id = GameState.get_currency_id(currency) + _label.text = "%s:" % GameState.get_currency_name(_currency_id) + _icon.texture = GameState.get_currency_icon(_currency_id) + _debug_button.text = "+100 %s" % GameState.get_currency_name(_currency_id) + + GameState.currency_changed.connect(_on_currency_changed) + _on_currency_changed(_currency_id, GameState.get_currency_amount_by_id(_currency_id)) + +func _on_currency_changed(changed_currency_id: StringName, amount: BigNumber) -> void: + if changed_currency_id != _currency_id: return _currency_label.text = amount.to_string_suffix(2) func _on_debug_income_button_pressed() -> void: - GameState.add_gold(BigNumber.from_float(100)) + if currency == null: + return + GameState.add_currency(currency, BigNumber.from_float(100)) diff --git a/currency_tile.tscn b/currency_tile.tscn index 02ddfd2..d1f0dbf 100644 --- a/currency_tile.tscn +++ b/currency_tile.tscn @@ -6,6 +6,11 @@ theme_override_constants/separation = 12 script = ExtResource("1_0hv07") +[node name="CurrencyIcon" type="TextureRect" parent="." unique_id=935299369] +layout_mode = 2 +custom_minimum_size = Vector2(20, 20) +stretch_mode = 5 + [node name="Label" type="Label" parent="." unique_id=89234840] layout_mode = 2 text = "Gold:" diff --git a/game_state.gd b/game_state.gd index 0382a25..6dcd353 100644 --- a/game_state.gd +++ b/game_state.gd @@ -4,40 +4,50 @@ extends Node # ========================================== # SIGNALS # ========================================== -signal currency_changed(currency_type: CurrencyType, new_amount: BigNumber) +signal currency_changed(currency_id: StringName, new_amount: BigNumber) signal generator_state_changed(generator_id: StringName, state: Dictionary) -enum CurrencyType { gold, gems } +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(CurrencyType.gold) + return _get_current_currency_ref(GOLD_CURRENCY_ID) set(value): - _set_current_currency(CurrencyType.gold, value) + _set_current_currency(GOLD_CURRENCY_ID, value) var gems: BigNumber: get: - return _get_current_currency_ref(CurrencyType.gems) + return _get_current_currency_ref(GEMS_CURRENCY_ID) set(value): - _set_current_currency(CurrencyType.gems, value) + _set_current_currency(GEMS_CURRENCY_ID, value) var total_gold_acquired: BigNumber: get: - return _get_total_currency_ref(CurrencyType.gold) + return _get_total_currency_ref(GOLD_CURRENCY_ID) set(value): - _set_total_currency(CurrencyType.gold, value) + _set_total_currency(GOLD_CURRENCY_ID, value) var total_gems_acquired: BigNumber: get: - return _get_total_currency_ref(CurrencyType.gems) + return _get_total_currency_ref(GEMS_CURRENCY_ID) set(value): - _set_total_currency(CurrencyType.gems, value) + _set_total_currency(GEMS_CURRENCY_ID, value) var generator_states: Dictionary = {} @@ -49,8 +59,12 @@ const GENERATOR_OWNED_KEY: String = "owned" const GENERATOR_PURCHASED_COUNT_KEY: String = "purchased_count" const GENERATOR_UNLOCKED_KEY: String = "unlocked" const GENERATOR_AVAILABLE_KEY: String = "available" +const CURRENCIES_KEY: String = "currencies" +const CURRENT_KEY: String = "current" +const TOTAL_KEY: String = "total" func _ready() -> void: + _initialize_currency_catalog() _initialize_currency_maps() load_game() @@ -58,39 +72,131 @@ func _ready() -> void: # STATE MODIFIERS # ========================================== func add_gold(amount: BigNumber) -> void: - add_currency(CurrencyType.gold, amount) + add_currency_by_id(GOLD_CURRENCY_ID, amount) func spend_gold(cost: BigNumber) -> bool: - return spend_currency(CurrencyType.gold, cost) + return spend_currency_by_id(GOLD_CURRENCY_ID, cost) func add_gems(amount: BigNumber) -> void: - add_currency(CurrencyType.gems, amount) + add_currency_by_id(GEMS_CURRENCY_ID, amount) func spend_gems(cost: BigNumber) -> bool: - return spend_currency(CurrencyType.gems, cost) + 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 + +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: + 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: + 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 add_currency(currency: Resource, amount: BigNumber) -> void: + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + push_warning("Attempted to add currency with an invalid Currency resource.") + return + + add_currency_by_id(currency_id, amount) + +func spend_currency(currency: Resource, cost: BigNumber) -> bool: + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + push_warning("Attempted to spend currency with an invalid Currency resource.") + return false + + return spend_currency_by_id(currency_id, cost) + +func add_currency_by_id(currency_id: StringName, amount: BigNumber) -> void: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return -func add_currency(currency: CurrencyType, amount: BigNumber) -> void: if amount.mantissa > 0.0: - _get_total_currency_ref(currency).add_in_place(amount) + _get_total_currency_ref(normalized_currency_id).add_in_place(amount) - var current: BigNumber = _get_current_currency_ref(currency) + var current: BigNumber = _get_current_currency_ref(normalized_currency_id) current.add_in_place(amount) - currency_changed.emit(currency, current) + currency_changed.emit(normalized_currency_id, current) -func spend_currency(currency: CurrencyType, cost: BigNumber) -> bool: - var current: BigNumber = _get_current_currency_ref(currency) +func spend_currency_by_id(currency_id: StringName, cost: BigNumber) -> bool: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return false + + var current: BigNumber = _get_current_currency_ref(normalized_currency_id) if current.is_greater_than(cost) or current.is_equal_to(cost): var negative_cost: BigNumber = BigNumber.new(-cost.mantissa, cost.exponent) current.add_in_place(negative_cost) - currency_changed.emit(currency, current) + currency_changed.emit(normalized_currency_id, current) return true return false -func get_currency_amount(currency: CurrencyType) -> BigNumber: - return _get_current_currency_ref(currency) +func get_currency_amount(currency: Resource) -> BigNumber: + return get_currency_amount_by_id(get_currency_id(currency)) -func get_total_currency_acquired(currency: CurrencyType) -> BigNumber: - return _get_total_currency_ref(currency) +func get_currency_amount_by_id(currency_id: StringName) -> BigNumber: + return _get_current_currency_ref(currency_id) + +func get_total_currency_acquired(currency: Resource) -> BigNumber: + return get_total_currency_acquired_by_id(get_currency_id(currency)) + +func get_total_currency_acquired_by_id(currency_id: StringName) -> BigNumber: + return _get_total_currency_ref(currency_id) func register_generator(generator_id: StringName, unlocked: bool = false, available: bool = false) -> void: var key: String = String(generator_id) @@ -167,6 +273,8 @@ 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(), @@ -174,25 +282,56 @@ func save_game() -> void: "generator_states": _serialize_generator_states(), "last_save_time": Time.get_unix_time_from_system() } - - var file = FileAccess.open(file_name, FileAccess.WRITE) + + var file: FileAccess = FileAccess.open(file_name, FileAccess.WRITE) if file: file.store_string(JSON.stringify(save_data)) func load_game() -> void: if not FileAccess.file_exists(file_name): return - - var file = FileAccess.open(file_name, FileAccess.READ) + + var file: FileAccess = FileAccess.open(file_name, FileAccess.READ) if file: - var parsed = JSON.parse_string(file.get_as_text()) + var parsed: Variant = JSON.parse_string(file.get_as_text()) if parsed is Dictionary: - gold = BigNumber.deserialize(parsed.get("gold", {})) - gems = BigNumber.deserialize(parsed.get("gems", {})) - total_gold_acquired = _deserialize_total_currency(parsed.get("total_gold_acquired", null), gold) - total_gems_acquired = _deserialize_total_currency(parsed.get("total_gems_acquired", null), gems) - generator_states = _deserialize_generator_states(parsed.get("generator_states", {})) - last_save_time = parsed.get("last_save_time", Time.get_unix_time_from_system()) + 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 + + var currencies: Dictionary = raw_currencies + for raw_currency_id in currencies.keys(): + var currency_id: StringName = _normalize_currency_id(StringName(String(raw_currency_id))) + if currency_id == &"": + continue + + var raw_entry: Variant = currencies.get(raw_currency_id) + if not (raw_entry is Dictionary): + continue + + var entry: Dictionary = raw_entry + var current_amount: BigNumber = BigNumber.deserialize(entry.get(CURRENT_KEY, {})) + var total_amount: BigNumber = _deserialize_total_currency(entry.get(TOTAL_KEY, null), current_amount) + _set_current_currency(currency_id, current_amount) + _set_total_currency(currency_id, total_amount) func _deserialize_total_currency(raw_total: Variant, current_amount: BigNumber) -> BigNumber: var minimum_total: BigNumber = _copy_big_number(current_amount) @@ -212,35 +351,109 @@ func _deserialize_total_currency(raw_total: Variant, current_amount: BigNumber) func _copy_big_number(value: BigNumber) -> BigNumber: return BigNumber.new(value.mantissa, value.exponent) +func _serialize_currency_balances() -> Dictionary: + var result: Dictionary = {} + + for currency_id in _collect_currency_ids_for_save(): + var key: String = String(currency_id) + if key.is_empty(): + continue + + result[key] = { + CURRENT_KEY: _get_current_currency_ref(currency_id).serialize(), + TOTAL_KEY: _get_total_currency_ref(currency_id).serialize(), + } + + return result + +func _collect_currency_ids_for_save() -> Array[StringName]: + var ids: Array[StringName] = [] + + for raw_id in _current_currency.keys(): + var normalized_currency_id: StringName = _normalize_currency_id(StringName(String(raw_id))) + if normalized_currency_id == &"" or ids.has(normalized_currency_id): + 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))) + 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: - _set_current_currency(CurrencyType.gold, BigNumber.from_float(0.0)) - _set_current_currency(CurrencyType.gems, BigNumber.from_float(0.0)) - _set_total_currency(CurrencyType.gold, BigNumber.from_float(0.0)) - _set_total_currency(CurrencyType.gems, BigNumber.from_float(0.0)) + for currency in DEFAULT_CURRENCIES: + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + continue + _set_current_currency(currency_id, BigNumber.from_float(0.0)) + _set_total_currency(currency_id, BigNumber.from_float(0.0)) -func _get_current_currency_ref(currency: CurrencyType) -> BigNumber: - var value: Variant = _current_currency.get(currency) +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() + +func _get_current_currency_ref(currency_id: StringName) -> BigNumber: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return BigNumber.from_float(0.0) + + var value: Variant = _current_currency.get(normalized_currency_id) if value is BigNumber: return value var fallback: BigNumber = BigNumber.from_float(0.0) - _current_currency[currency] = fallback + _current_currency[normalized_currency_id] = fallback return fallback -func _get_total_currency_ref(currency: CurrencyType) -> BigNumber: - var value: Variant = _total_currency_acquired.get(currency) +func _get_total_currency_ref(currency_id: StringName) -> BigNumber: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return BigNumber.from_float(0.0) + + var value: Variant = _total_currency_acquired.get(normalized_currency_id) if value is BigNumber: return value var fallback: BigNumber = BigNumber.from_float(0.0) - _total_currency_acquired[currency] = fallback + _total_currency_acquired[normalized_currency_id] = fallback return fallback -func _set_current_currency(currency: CurrencyType, value: BigNumber) -> void: - _current_currency[currency] = _copy_big_number(value) +func _set_current_currency(currency_id: StringName, value: BigNumber) -> void: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return + _current_currency[normalized_currency_id] = _copy_big_number(value) -func _set_total_currency(currency: CurrencyType, value: BigNumber) -> void: - _total_currency_acquired[currency] = _copy_big_number(value) +func _set_total_currency(currency_id: StringName, value: BigNumber) -> void: + var normalized_currency_id: StringName = _normalize_currency_id(currency_id) + if normalized_currency_id == &"": + return + _total_currency_acquired[normalized_currency_id] = _copy_big_number(value) func _default_generator_state(unlocked: bool, available: bool) -> Dictionary: return { diff --git a/generator-unlock-goals/generator_unlock_system.gd b/generator-unlock-goals/generator_unlock_system.gd index b9c650e..1d417c1 100644 --- a/generator-unlock-goals/generator_unlock_system.gd +++ b/generator-unlock-goals/generator_unlock_system.gd @@ -1,11 +1,11 @@ extends Node class UnlockGoalRequirement extends RefCounted: - var currency: GameState.CurrencyType + var currency_id: StringName var amount: BigNumber - func _init(req_currency: GameState.CurrencyType, req_amount: BigNumber) -> void: - currency = req_currency + func _init(req_currency_id: StringName, req_amount: BigNumber) -> void: + currency_id = req_currency_id amount = req_amount class UnlockGoal extends RefCounted: @@ -31,7 +31,7 @@ func _ready() -> void: GameState.currency_changed.connect(_on_currency_changed) _evaluate_all_goals() -func _on_currency_changed(_changed_currency: GameState.CurrencyType, _new_amount: BigNumber) -> void: +func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void: _evaluate_all_goals() func _evaluate_all_goals() -> void: @@ -48,7 +48,7 @@ func _evaluate_goal(goal: UnlockGoal) -> bool: return false for requirement in goal.requirements: - var total_amount: BigNumber = _get_total_currency_amount(requirement.currency) + var total_amount: BigNumber = _get_total_currency_amount(requirement.currency_id) if total_amount.is_less_than(requirement.amount): return false @@ -159,8 +159,8 @@ func _parse_requirement(raw_requirement: Variant, goal_id: String) -> UnlockGoal return null var requirement_dict: Dictionary = raw_requirement - var currency_value: int = _parse_currency(requirement_dict.get("currency", "")) - if currency_value < 0: + var currency_id: StringName = _parse_currency(requirement_dict.get("currency", "")) + if currency_id == &"" or not GameState.is_known_currency_id(currency_id): push_warning("Skipping requirement in goal '%s': unknown currency '%s'" % [goal_id, String(requirement_dict.get("currency", ""))]) return null @@ -174,26 +174,13 @@ func _parse_requirement(raw_requirement: Variant, goal_id: String) -> UnlockGoal push_warning("Skipping requirement in goal '%s': amount must be non-negative" % goal_id) return null - return UnlockGoalRequirement.new(currency_value, amount) + return UnlockGoalRequirement.new(currency_id, amount) -func _parse_currency(raw_currency: Variant) -> int: - if raw_currency is int: - var currency_int: int = raw_currency - if currency_int == GameState.CurrencyType.gold or currency_int == GameState.CurrencyType.gems: - return currency_int - return -1 +func _parse_currency(raw_currency: Variant) -> StringName: + return GameState.parse_currency_id(raw_currency) - var currency_text: String = String(raw_currency).to_lower().strip_edges() - match currency_text: - "gold": - return GameState.CurrencyType.gold - "gems", "gem": - return GameState.CurrencyType.gems - _: - return -1 - -func _get_total_currency_amount(currency: GameState.CurrencyType) -> BigNumber: - return GameState.get_total_currency_acquired(currency) +func _get_total_currency_amount(currency_id: StringName) -> BigNumber: + return GameState.get_total_currency_acquired_by_id(currency_id) func _collect_known_generator_ids() -> void: _known_generator_ids.clear() diff --git a/generator-unlock-goals/goals_debug_ui.gd b/generator-unlock-goals/goals_debug_ui.gd index 8c78b23..c6ba047 100644 --- a/generator-unlock-goals/goals_debug_ui.gd +++ b/generator-unlock-goals/goals_debug_ui.gd @@ -1,11 +1,11 @@ extends PanelContainer class GoalRequirement extends RefCounted: - var currency: GameState.CurrencyType + var currency_id: StringName var amount: BigNumber - func _init(req_currency: GameState.CurrencyType, req_amount: BigNumber) -> void: - currency = req_currency + func _init(req_currency_id: StringName, req_amount: BigNumber) -> void: + currency_id = req_currency_id amount = req_amount class GoalDefinition extends RefCounted: @@ -47,7 +47,7 @@ func _ready() -> void: _refresh_ui() _refresh_ui.call_deferred() -func _on_currency_changed(_currency_type: GameState.CurrencyType, _new_amount: BigNumber) -> 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: @@ -119,8 +119,8 @@ func _parse_requirement(raw_requirement: Variant) -> GoalRequirement: return null var requirement_dict: Dictionary = raw_requirement - var currency_value: int = _parse_currency(requirement_dict.get("currency", "")) - if currency_value < 0: + var currency_id: StringName = _parse_currency(requirement_dict.get("currency", "")) + if currency_id == &"" or not GameState.is_known_currency_id(currency_id): return null var raw_amount: Variant = requirement_dict.get("amount", {}) @@ -131,23 +131,10 @@ func _parse_requirement(raw_requirement: Variant) -> GoalRequirement: if amount.mantissa < 0.0: return null - return GoalRequirement.new(currency_value, amount) + return GoalRequirement.new(currency_id, amount) -func _parse_currency(raw_currency: Variant) -> int: - if raw_currency is int: - var currency_int: int = raw_currency - if currency_int == GameState.CurrencyType.gold or currency_int == GameState.CurrencyType.gems: - return currency_int - return -1 - - var currency_text: String = String(raw_currency).to_lower().strip_edges() - match currency_text: - "gold": - return GameState.CurrencyType.gold - "gems", "gem": - return GameState.CurrencyType.gems - _: - return -1 +func _parse_currency(raw_currency: Variant) -> StringName: + return GameState.parse_currency_id(raw_currency) func _build_goal_rows() -> void: _rows_by_goal_id.clear() @@ -225,7 +212,7 @@ func _refresh_goal_row(row: GoalRow) -> void: func _is_goal_met(goal: GoalDefinition) -> bool: for requirement in goal.requirements: - var total_amount: BigNumber = GameState.get_total_currency_acquired(requirement.currency) + var total_amount: BigNumber = GameState.get_total_currency_acquired_by_id(requirement.currency_id) if total_amount.is_less_than(requirement.amount): return false return true @@ -253,21 +240,15 @@ func _on_unlock_pressed(goal_id: StringName) -> void: func _get_requirements_text(goal: GoalDefinition) -> String: var parts: Array[String] = [] for requirement in goal.requirements: - var total_amount: BigNumber = GameState.get_total_currency_acquired(requirement.currency) + var total_amount: BigNumber = GameState.get_total_currency_acquired_by_id(requirement.currency_id) parts.append( "%s %s / %s" % [ - _currency_label(requirement.currency), + _currency_label(requirement.currency_id), total_amount.to_string_suffix(2), requirement.amount.to_string_suffix(2) ] ) return " | ".join(parts) -func _currency_label(currency: GameState.CurrencyType) -> String: - match currency: - GameState.CurrencyType.gold: - return "Gold" - GameState.CurrencyType.gems: - return "Gems" - _: - return "Unknown" +func _currency_label(currency_id: StringName) -> String: + return GameState.get_currency_name(currency_id) diff --git a/generator/currency_generator.gd b/generator/currency_generator.gd index 499d5ee..6020256 100644 --- a/generator/currency_generator.gd +++ b/generator/currency_generator.gd @@ -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: GameState.CurrencyType = GameState.CurrencyType.gold +@export var currency: Resource = GameState.GOLD_CURRENCY ## Data resource containing balancing values and defaults (required). @export var data: CurrencyGeneratorData ## Enables per-cycle automatic production when true. @@ -85,6 +85,9 @@ func _ready() -> void: _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)) @@ -247,40 +250,35 @@ func get_generator_id() -> StringName: _ensure_registered() return _generator_id +## Returns the configured currency id. +func get_currency_id() -> StringName: + return GameState.get_currency_id(currency) + ## True when the generator is both unlocked and available. func is_available_to_player() -> bool: return is_unlocked and is_available ## Routes positive/negative currency deltas to the configured currency bucket. func _add_currency(amount: BigNumber) -> void: - match currency: - GameState.CurrencyType.gold: - GameState.add_gold(amount) - GameState.CurrencyType.gems: - GameState.add_gems(amount) - _: - push_error("Unknown currency type in CurrencyGenerator._add_currency") + if currency == null: + push_error("CurrencyGenerator '%s' cannot add currency: currency resource is null." % String(name)) + return + + GameState.add_currency(currency, amount) ## Attempts to spend target currency; returns false when insufficient. func _spend_currency(cost: BigNumber) -> bool: - match currency: - GameState.CurrencyType.gold: - return GameState.spend_gold(cost) - GameState.CurrencyType.gems: - return GameState.spend_gems(cost) - _: - push_error("Unknown currency type in CurrencyGenerator._spend_currency") - return false + if currency == null: + push_error("CurrencyGenerator '%s' cannot spend currency: currency resource is null." % String(name)) + return false + + return GameState.spend_currency(currency, cost) ## Returns the current amount for the configured currency type. func _get_currency_amount() -> BigNumber: - match currency: - GameState.CurrencyType.gold: - return GameState.gold - GameState.CurrencyType.gems: - return GameState.gems - _: - return BigNumber.from_float(0.0) + if currency == null: + return BigNumber.from_float(0.0) + return GameState.get_currency_amount(currency) ## Safely converts finite float values into BigNumber costs. func _float_to_big_number(value: float) -> BigNumber: diff --git a/generator_container.gd b/generator_container.gd index db1abcb..83d6f3c 100644 --- a/generator_container.gd +++ b/generator_container.gd @@ -32,8 +32,8 @@ func _on_debug_income_pressed() -> void: _generator.grant_currency(BigNumber.from_float(100.0)) _refresh_generator_ui() -func _on_currency_changed(changed_currency: GameState.CurrencyType, _new_value: BigNumber) -> void: - if changed_currency != _generator.currency: +func _on_currency_changed(changed_currency_id: StringName, _new_value: BigNumber) -> void: + if changed_currency_id != _generator.get_currency_id(): return _refresh_generator_ui() diff --git a/generator_museum.tscn b/generator_museum.tscn index f330cad..7f58d88 100644 --- a/generator_museum.tscn +++ b/generator_museum.tscn @@ -7,6 +7,7 @@ [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) @@ -29,7 +30,7 @@ shape = SubResource("RectangleShape2D_y5m1q") [node name="GemsGenerator" type="Node2D" parent="." unique_id=931294956] script = ExtResource("1_pl17p") -currency = 1 +currency = ExtResource("6_gemsc") data = ExtResource("3_dc82g") [node name="UI" type="Control" parent="." unique_id=452530906] @@ -42,17 +43,16 @@ offset_bottom = 40.0 layout_mode = 0 offset_right = 160.0 offset_bottom = 31.0 -data = ExtResource("2_x5lt1") [node name="GemsCurrencyTile" parent="UI" unique_id=1977342362 instance=ExtResource("3_x5lt1")] layout_mode = 0 offset_top = 28.0 offset_right = 160.0 offset_bottom = 59.0 -currency = 1 -data = ExtResource("3_dc82g") +currency = ExtResource("6_gemsc") [node name="GoldGeneratorContainer" parent="UI" unique_id=1129190041 node_paths=PackedStringArray("_generator") instance=ExtResource("3_pw2a0")] +visible = false layout_mode = 1 offset_left = -19.0 offset_top = 53.0 @@ -61,6 +61,7 @@ offset_bottom = 218.0 _generator = NodePath("../../GoldGenerator") [node name="GemsGeneratorContainer" 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 @@ -69,6 +70,7 @@ offset_bottom = 380.0 _generator = NodePath("../../GemsGenerator") [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