Decouple everything from globals

This commit is contained in:
2026-04-10 01:09:57 +02:00
parent b758803afe
commit f671ab3419
56 changed files with 993 additions and 1291 deletions

View File

@@ -44,42 +44,45 @@ var _mouse_entered: bool = false
## Time left until next click/hover grant is allowed.
var _remaining_click_cooldown_seconds: float = 0.0
## Reference to LevelGameState instance.
@onready var game_state: LevelGameState = find_parent("LevelGameState")
## Number of currently owned generators.
## Backed by GameState via this generator's resolved id.
## Backed by LevelGameState via this generator's resolved id.
var owned: int:
get:
_ensure_registered()
return GameState.get_generator_owned(_generator_id)
return game_state.get_generator_owned(_generator_id)
set(value):
_ensure_registered()
GameState.set_generator_owned(_generator_id, value)
game_state.set_generator_owned(_generator_id, value)
## Lifetime purchased generators (does not decrease if ownership drops).
var purchased_count: int:
get:
_ensure_registered()
return GameState.get_generator_purchased_count(_generator_id)
return game_state.get_generator_purchased_count(_generator_id)
set(value):
_ensure_registered()
GameState.set_generator_purchased_count(_generator_id, value)
game_state.set_generator_purchased_count(_generator_id, value)
## Whether this generator is unlocked for the player.
var is_unlocked: bool:
get:
_ensure_registered()
return GameState.is_generator_unlocked(_generator_id)
return game_state.is_generator_unlocked(_generator_id)
set(value):
_ensure_registered()
GameState.set_generator_unlocked(_generator_id, value)
game_state.set_generator_unlocked(_generator_id, value)
## Whether this unlocked generator is currently visible/usable to the player.
var is_available: bool:
get:
_ensure_registered()
return GameState.is_generator_available(_generator_id)
return game_state.is_generator_available(_generator_id)
set(value):
_ensure_registered()
GameState.set_generator_available(_generator_id, value)
game_state.set_generator_available(_generator_id, value)
## Accumulated elapsed time toward the next automatic production cycle.
var cycle_progress_seconds: float = 0.0
@@ -98,9 +101,10 @@ func _ready() -> void:
_generator_id = _resolve_generator_id()
cycle_progress_seconds = 0.0
GameState.currency_changed.connect(_on_currency_changed)
GameState.generator_state_changed.connect(_on_generated_state_changed)
GameState.goal_completed.connect(_on_goal_completed)
if game_state:
game_state.currency_changed.connect(_on_currency_changed)
game_state.generator_state_changed.connect(_on_generated_state_changed)
game_state.goal_completed.connect(_on_goal_completed)
_ensure_registered()
@@ -220,18 +224,27 @@ func buy_max() -> int:
func get_buffs() -> Array[GeneratorBuffData]:
if data == null:
return []
return GameState.get_buffs_for_generator(_generator_id)
if game_state == null:
return []
return game_state.get_buffs_for_generator(_generator_id)
func get_buff_level(buff_id: StringName) -> int:
_ensure_registered()
return GameState.get_buff_level(buff_id)
if game_state == null:
return 0
return game_state.get_buff_level(buff_id)
func is_buff_unlocked(buff_id: StringName) -> bool:
_ensure_registered()
return GameState.is_buff_unlocked(buff_id)
if game_state == null:
return false
return game_state.is_buff_unlocked(buff_id)
func get_buff_cost(buff_id: StringName) -> BigNumber:
var buff: GeneratorBuffData = GameState.get_buff(buff_id)
if game_state == null:
return BigNumber.from_float(0.0)
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
if buff == null:
return BigNumber.from_float(0.0)
if not is_buff_unlocked(buff.id):
@@ -241,7 +254,10 @@ func get_buff_cost(buff_id: StringName) -> BigNumber:
return buff.get_cost_for_level(level)
func get_buff_cost_currency_id(buff_id: StringName) -> StringName:
var buff: GeneratorBuffData = GameState.get_buff(buff_id)
if game_state == null:
return &""
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
if buff == null:
return &""
return _resolve_buff_cost_currency_id(buff)
@@ -250,7 +266,10 @@ func can_buy_buff(buff_id: StringName) -> bool:
if not is_available_to_player():
return false
var buff: GeneratorBuffData = GameState.get_buff(buff_id)
if game_state == null:
return false
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
if buff == null:
return false
@@ -266,21 +285,24 @@ func can_buy_buff(buff_id: StringName) -> bool:
return false
var cost: BigNumber = buff.get_cost_for_level(current_level)
var current_amount: BigNumber = GameState.get_currency_amount_by_id(cost_currency_id)
var current_amount: BigNumber = game_state.get_currency_amount_by_id(cost_currency_id)
return current_amount.is_greater_than(cost) or current_amount.is_equal_to(cost)
func buy_buff(buff_id: StringName) -> bool:
if not is_available_to_player():
return false
var buff: GeneratorBuffData = GameState.get_buff(buff_id)
if game_state == null:
return false
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
if buff == null:
return false
if not GameState.is_buff_unlocked(buff.id):
if not game_state.is_buff_unlocked(buff.id):
return false
var current_level: int = GameState.get_buff_level(buff.id)
var current_level: int = game_state.get_buff_level(buff.id)
if not buff.can_purchase_next_level(current_level):
return false
@@ -294,19 +316,22 @@ func buy_buff(buff_id: StringName) -> bool:
return false
var next_level: int = current_level + 1
GameState.set_buff_level(buff.id, next_level)
game_state.set_buff_level(buff.id, next_level)
_apply_resource_purchase_buff(buff, next_level)
buff_purchased.emit(buff.id, next_level, cost, cost_currency_id)
return true
func is_buff_maxed(buff_id: StringName) -> bool:
var buff: GeneratorBuffData = GameState.get_buff(buff_id)
if game_state == null:
return true
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
if buff == null:
return true
if not GameState.is_buff_unlocked(buff.id):
if not game_state.is_buff_unlocked(buff.id):
return false
return not buff.can_purchase_next_level(GameState.get_buff_level(buff.id))
return not buff.can_purchase_next_level(game_state.get_buff_level(buff.id))
func get_automatic_production_multiplier() -> float:
return _get_multiplier_for_buff_kind(GeneratorBuffData.BuffKind.AUTO_PRODUCTION_MULTIPLIER)
@@ -384,13 +409,18 @@ func _grants_click_while_hovering() -> bool:
return grants_click_while_hovering
func _get_prestige_multiplier() -> float:
var manager: PrestigeManager = get_node_or_null("/root/PrestigeManager")
if manager == null:
if game_state == null:
return 1.0
if not manager.get_total_multiplier():
var parent: Node = get_parent()
if parent == null:
return 1.0
var raw_multiplier: Variant = manager.get_total_multiplier()
var prestige_manager: PrestigeManager = parent.find_child("PrestigeManager", true, false)
if prestige_manager == null:
return 1.0
var raw_multiplier: Variant = prestige_manager.get_total_multiplier()
if raw_multiplier is float:
return maxf(raw_multiplier, 0.0)
if raw_multiplier is int:
@@ -405,14 +435,16 @@ func get_generator_id() -> StringName:
## Returns the configured currency id.
func get_currency_id() -> StringName:
return GameState.get_currency_id(currency)
if game_state == null:
return &""
return game_state.get_currency_id(currency)
func get_purchase_currency_id() -> StringName:
var purchase_currency: Currency = _resolve_purchase_currency()
if purchase_currency == null:
return &""
return GameState.get_currency_id(purchase_currency)
return game_state.get_currency_id(purchase_currency)
## True when the generator is both unlocked and available.
func is_available_to_player() -> bool:
@@ -424,13 +456,17 @@ func _add_currency(amount: BigNumber) -> void:
push_error("CurrencyGenerator '%s' cannot add currency: currency resource is null." % String(name))
return
GameState.add_currency(currency, amount)
if game_state == null:
return
game_state.add_currency(currency, amount)
func _add_currency_by_id(currency_id: StringName, amount: BigNumber) -> void:
if currency_id == &"":
return
GameState.add_currency_by_id(currency_id, amount)
if game_state == null:
return
game_state.add_currency_by_id(currency_id, amount)
## Attempts to spend target currency; returns false when insufficient.
func _spend_currency(cost: BigNumber) -> bool:
@@ -438,25 +474,33 @@ func _spend_currency(cost: BigNumber) -> bool:
push_error("CurrencyGenerator '%s' cannot spend currency: currency resource is null." % String(name))
return false
return GameState.spend_currency(currency, cost)
if game_state == null:
return false
return game_state.spend_currency(currency, cost)
func _spend_currency_by_id(currency_id: StringName, cost: BigNumber) -> bool:
if currency_id == &"":
return false
return GameState.spend_currency_by_id(currency_id, cost)
if game_state == null:
return false
return game_state.spend_currency_by_id(currency_id, cost)
## Returns the current amount for the configured currency type.
func _get_currency_amount() -> BigNumber:
if currency == null:
return BigNumber.from_float(0.0)
return GameState.get_currency_amount(currency)
if game_state == null:
return BigNumber.from_float(0.0)
return game_state.get_currency_amount(currency)
func _get_currency_amount_by_id(currency_id: StringName) -> BigNumber:
if currency_id == &"":
return BigNumber.from_float(0.0)
return GameState.get_currency_amount_by_id(currency_id)
if game_state == null:
return BigNumber.from_float(0.0)
return game_state.get_currency_amount_by_id(currency_id)
## Safely converts finite float values into BigNumber costs.
func _float_to_big_number(value: float) -> BigNumber:
@@ -484,7 +528,9 @@ func _resolve_buff_cost_currency_id(buff: GeneratorBuffData) -> StringName:
if cost_currency == null:
return &""
return GameState.get_currency_id(cost_currency)
if game_state == null:
return &""
return game_state.get_currency_id(cost_currency)
func _resolve_purchase_currency() -> Currency:
if data == null:
@@ -500,10 +546,14 @@ func _resolve_buff_target_currency_id(buff: GeneratorBuffData) -> StringName:
if target_currency == null:
return &""
return GameState.get_currency_id(target_currency)
if game_state == null:
return &""
return game_state.get_currency_id(target_currency)
func _get_multiplier_for_buff_kind(kind: GeneratorBuffData.BuffKind) -> float:
return GameState.get_effective_multiplier(_generator_id, kind)
if game_state == null:
return 1.0
return game_state.get_effective_multiplier(_generator_id, kind)
func _apply_resource_purchase_buff(buff: GeneratorBuffData, level: int) -> void:
if buff == null:
@@ -533,7 +583,7 @@ func _resolve_generator_id() -> StringName:
return &"generator"
## Ensures this generator has a state entry in GameState.
## Ensures this generator has a state entry in LevelGameState.
func _ensure_registered() -> void:
if _is_registered or _is_registering:
return
@@ -543,8 +593,11 @@ func _ensure_registered() -> void:
if _generator_id == &"":
return
if game_state == null:
return
_is_registering = true
GameState.register_generator(
game_state.register_generator(
_generator_id,
_default_unlocked_state(),
_default_available_state(),
@@ -555,6 +608,9 @@ func _ensure_registered() -> void:
_is_registering = false
func _register_buffs() -> void:
if game_state == null:
return
for buff in get_buffs():
if buff == null:
continue
@@ -564,20 +620,24 @@ func _register_buffs() -> void:
continue
var buff_id: StringName = StringName(buff_id_text)
GameState.register_buff(buff)
game_state.register_buff(buff)
if not GameState.is_buff_unlocked(buff_id):
var persisted_level: int = GameState.get_buff_level(buff_id)
if not game_state.is_buff_unlocked(buff_id):
var persisted_level: int = game_state.get_buff_level(buff_id)
var starts_unlocked: bool = buff.unlocked or persisted_level > 0
GameState.set_buff_unlocked(buff_id, starts_unlocked)
game_state.set_buff_unlocked(buff_id, starts_unlocked)
_try_unlock_buff_from_goal(buff)
func _try_unlock_buff_from_goal(buff: GeneratorBuffData) -> void:
GameState._try_unlock_buff_from_goal(buff)
if game_state == null:
return
game_state._try_unlock_buff_from_goal(buff)
func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void:
GameState.evaluate_all_goals()
if game_state == null:
return
game_state.evaluate_all_goals()
func _on_goal_completed(goal_id: StringName) -> void:
if data == null or not data.has_unlock_goal():
@@ -587,10 +647,12 @@ func _on_goal_completed(goal_id: StringName) -> void:
if is_available_to_player():
return
GameState.set_generator_unlocked(_generator_id, true)
GameState.set_generator_available(_generator_id, true)
if game_state == null:
return
game_state.set_generator_unlocked(_generator_id, true)
game_state.set_generator_available(_generator_id, true)
goal_achieved.emit(_generator_id, goal_id)
_on_generated_state_changed(_generator_id, GameState._get_generator_state(_generator_id))
_on_generated_state_changed(_generator_id, game_state._get_generator_state(_generator_id))
## Default unlocked state loaded from generator data.
func _default_unlocked_state() -> bool:

View File

@@ -58,10 +58,10 @@ func get_unlock_goal_amount() -> BigNumber:
if unlock_goal == null:
return BigNumber.from_float(0.0)
var valid_requirements: Array[GoalRequirementData] = unlock_goal.get_valid_requirements()
if valid_requirements.is_empty():
var requirements: Array[GoalRequirementData] = unlock_goal.get_requirements()
if requirements.is_empty():
return BigNumber.from_float(0.0)
var requirement_resource: GoalRequirementData = valid_requirements[0]
var requirement_resource: GoalRequirementData = requirements[0]
if requirement_resource == null:
return BigNumber.from_float(0.0)
@@ -75,10 +75,10 @@ func get_unlock_goal_currency() -> Currency:
if unlock_goal == null:
return null
var valid_requirements: Array[GoalRequirementData] = unlock_goal.get_valid_requirements()
if valid_requirements.is_empty():
var requirements: Array[GoalRequirementData] = unlock_goal.get_requirements()
if requirements.is_empty():
return null
var requirement_resource: GoalRequirementData = valid_requirements[0]
var requirement_resource: GoalRequirementData = requirements[0]
if requirement_resource == null:
return null

View File

@@ -37,11 +37,12 @@ func _ready() -> void:
_generator.purchase_failed.connect(_on_generator_updated)
_generator.buff_purchased.connect(_on_generator_updated)
_generator.buff_purchase_failed.connect(_on_generator_updated)
GameState.currency_changed.connect(_on_currency_changed)
GameState.generator_state_changed.connect(_on_generator_state_changed)
GameState.generator_buff_level_changed.connect(_on_generator_buff_level_changed)
GameState.generator_buff_unlocked_changed.connect(_on_generator_buff_unlocked_changed)
if _generator.game_state:
_generator.game_state.currency_changed.connect(_on_currency_changed)
_generator.game_state.generator_state_changed.connect(_on_generator_state_changed)
_generator.game_state.generator_buff_level_changed.connect(_on_generator_buff_level_changed)
_generator.game_state.generator_buff_unlocked_changed.connect(_on_generator_buff_unlocked_changed)
_name_label.text = _generator.data.name if _generator.data != null else "Generator"
_build_buff_rows()
@@ -123,9 +124,9 @@ func _refresh_buff_rows(can_interact: bool) -> void:
if not buff_unlocked:
var locked_hint: String = "Locked"
var unlock_goal_currency: Resource = buff.get_unlock_goal_currency()
if buff.has_unlock_goal() and unlock_goal_currency != null:
var goal_currency_id: StringName = GameState.get_currency_id(unlock_goal_currency)
var currency_name: String = GameState.get_currency_name(goal_currency_id)
if buff.has_unlock_goal() and unlock_goal_currency != null and _generator.game_state:
var goal_currency_id: StringName = _generator.game_state.get_currency_id(unlock_goal_currency)
var currency_name: String = _generator.game_state.get_currency_name(goal_currency_id)
var required_text: String = buff.get_unlock_goal_amount().to_string_suffix(2)
locked_hint = "Unlock at %s %s" % [required_text, currency_name]
@@ -141,7 +142,7 @@ func _refresh_buff_rows(can_interact: bool) -> void:
var cost: BigNumber = _generator.get_buff_cost(buff.id)
var cost_currency_id: StringName = _generator.get_buff_cost_currency_id(buff.id)
var currency_label: String = GameState.get_currency_name(cost_currency_id)
var currency_label: String = _generator.game_state.get_currency_name(cost_currency_id) if _generator.game_state else "Unknown"
var cost_text: String = "%s %s" % [cost.to_string_suffix(2), currency_label]
var can_buy: bool = can_interact and _generator.can_buy_buff(buff.id)
row.tile.set_runtime(level, effect_text, cost_text, can_buy, false)
@@ -161,7 +162,7 @@ func _refresh_generator_ui() -> void:
_owned_value.text = str(_generator.owned)
if can_interact:
var purchase_currency_id: StringName = _generator.get_purchase_currency_id()
var purchase_currency_name: String = GameState.get_currency_name(purchase_currency_id)
var purchase_currency_name: String = _generator.game_state.get_currency_name(purchase_currency_id) if _generator.game_state else "Unknown"
if purchase_currency_name.is_empty():
purchase_currency_name = "Unconfigured"
_next_cost_value.text = "%s %s" % [_generator.get_next_cost().to_string_suffix(2), purchase_currency_name]