systems rework: buffs, prestige graph, research, modular architecture
Decouples core systems from centralized globals in favor of catalogue-based architecture and data-driven content. Key changes: - Prestige: node-based buff tech tree, graph panel, progress bar - Research: multi-worker system, per-generator research, xp generation - Ascension: meta-currency layer via multi-currency prestige resets - Buffs: refactored as generator-independent via catalogues - UI: currency panel, edge-scrolling camera + zoom, current-goal panel - Alchemy Tower: crafting building with recipe/cost system - Testing: 7 test suites (ascension, prestige, research, goals) - Content: migrated from hardcoded idles/ to gym format (tiny_sword)
This commit was merged in pull request #1.
This commit is contained in:
@@ -24,7 +24,7 @@ signal goal_achieved(generator_id: StringName, goal_id: StringName)
|
||||
const HUGE_COST_EXPONENT: int = 1000000
|
||||
|
||||
## Currency target updated by this generator.
|
||||
@export var currency: Resource
|
||||
@export var currency: Currency
|
||||
## Data resource containing balancing values and defaults (required).
|
||||
@export var data: CurrencyGeneratorData
|
||||
## Enables per-cycle automatic production when true.
|
||||
@@ -37,49 +37,52 @@ const HUGE_COST_EXPONENT: int = 1000000
|
||||
@export var run_multiplier: float = 1.0
|
||||
|
||||
## Reference to generator UI
|
||||
@onready var info_generator_container: GeneratorPanel = $GeneratorPanel
|
||||
@export var info_generator_container: GeneratorPanel
|
||||
|
||||
## True while the pointer is inside the generator Area2D.
|
||||
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
|
||||
@@ -96,12 +99,15 @@ func _ready() -> void:
|
||||
assert(data != null, "Data cannot be null")
|
||||
|
||||
_generator_id = _resolve_generator_id()
|
||||
#_ensure_registered()
|
||||
cycle_progress_seconds = 0.0
|
||||
|
||||
GameState.currency_changed.connect(_on_currency_changed)
|
||||
GameState.generator_state_changed.connect(_on_generated_state_changed)
|
||||
_evaluate_generator_unlock_goal(false)
|
||||
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)
|
||||
# Research XP is awarded via game_state.add_research_xp() - buff calculation handled statically
|
||||
|
||||
_ensure_registered()
|
||||
|
||||
## Updates click cooldown/click grants and runs automatic production cycles.
|
||||
func _process(delta: float) -> void:
|
||||
@@ -137,6 +143,23 @@ func _grant_cycle_income(cycle_count: int) -> void:
|
||||
var produced: BigNumber = BigNumber.from_float(per_cycle).multiply(BigNumber.from_float(float(cycle_count)))
|
||||
_add_currency(produced)
|
||||
production_tick.emit(produced, cycle_count, owned)
|
||||
|
||||
if data != null and data.research_data != null:
|
||||
if not _is_research_active(data.research_data.id):
|
||||
return
|
||||
|
||||
var research_workers: int = game_state.get_research_workers()
|
||||
if research_workers < data.research_data.min_workers_for_xp:
|
||||
return
|
||||
|
||||
var amount_float: float = produced.mantissa * pow(10.0, float(produced.exponent))
|
||||
var base_xp: BigNumber = BigNumber.from_float(data.research_data.xp_per_currency_produced * amount_float)
|
||||
|
||||
var worker_multiplier: float = 1.0 + (float(research_workers) * data.research_data.worker_scaling_factor)
|
||||
var scaled_xp: BigNumber = base_xp.multiply(BigNumber.from_float(worker_multiplier))
|
||||
|
||||
if game_state != null and scaled_xp.mantissa > 0.0:
|
||||
game_state.add_research_xp(data.research_data.id, scaled_xp)
|
||||
|
||||
## Convenience helper for the next single-unit purchase cost.
|
||||
func get_next_cost() -> BigNumber:
|
||||
@@ -195,7 +218,7 @@ func buy_max() -> int:
|
||||
if purchase_currency_id == &"":
|
||||
return 0
|
||||
|
||||
var available_currency: float = _big_number_to_float(_get_currency_amount_by_id(purchase_currency_id))
|
||||
var available_currency: float = _get_currency_amount_by_id(purchase_currency_id).to_float()
|
||||
var estimated_max: int = data.max_affordable(owned, available_currency)
|
||||
if estimated_max <= 0:
|
||||
return 0
|
||||
@@ -219,18 +242,27 @@ func buy_max() -> int:
|
||||
func get_buffs() -> Array[GeneratorBuffData]:
|
||||
if data == null:
|
||||
return []
|
||||
return data.buffs
|
||||
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_generator_buff_level(_generator_id, 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_generator_buff_unlocked(_generator_id, 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 = _find_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):
|
||||
@@ -240,7 +272,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 = _find_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)
|
||||
@@ -249,7 +284,10 @@ func can_buy_buff(buff_id: StringName) -> bool:
|
||||
if not is_available_to_player():
|
||||
return false
|
||||
|
||||
var buff: GeneratorBuffData = _find_buff(buff_id)
|
||||
if game_state == null:
|
||||
return false
|
||||
|
||||
var buff: GeneratorBuffData = game_state.get_buff(buff_id)
|
||||
if buff == null:
|
||||
return false
|
||||
|
||||
@@ -265,21 +303,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 = _find_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 is_buff_unlocked(buff.id):
|
||||
if not game_state.is_buff_unlocked(buff.id):
|
||||
return false
|
||||
|
||||
var current_level: int = 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
|
||||
|
||||
@@ -293,25 +334,58 @@ func buy_buff(buff_id: StringName) -> bool:
|
||||
return false
|
||||
|
||||
var next_level: int = current_level + 1
|
||||
GameState.set_generator_buff_level(_generator_id, 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 = _find_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 is_buff_unlocked(buff.id):
|
||||
if not game_state.is_buff_unlocked(buff.id):
|
||||
return false
|
||||
|
||||
return not buff.can_purchase_next_level(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)
|
||||
|
||||
func get_effective_auto_run_multiplier() -> float:
|
||||
return run_multiplier * get_automatic_production_multiplier() * _get_prestige_multiplier()
|
||||
return run_multiplier * get_research_multiplier() * get_automatic_production_multiplier() * _get_prestige_buff_production_multiplier()
|
||||
|
||||
func get_research_multiplier() -> float:
|
||||
if data == null or data.research_data == null:
|
||||
return 1.0
|
||||
if game_state == null:
|
||||
return 1.0
|
||||
return game_state.get_research_multiplier(data.research_data.id)
|
||||
|
||||
func _is_research_active(research_id: StringName) -> bool:
|
||||
if game_state == null:
|
||||
return true
|
||||
|
||||
var research_panel: Node = null
|
||||
var parent: Node = get_parent()
|
||||
while parent != null and parent != game_state.get_tree().root:
|
||||
if parent.has_method("is_research_active"):
|
||||
research_panel = parent
|
||||
break
|
||||
parent = parent.get_parent()
|
||||
|
||||
if research_panel == null:
|
||||
research_panel = game_state.find_child("ResearchPanel", true, false)
|
||||
|
||||
if research_panel == null:
|
||||
return true
|
||||
|
||||
if research_panel.has_method("is_research_active"):
|
||||
return research_panel.is_research_active(research_id)
|
||||
|
||||
return true
|
||||
|
||||
func reset_runtime_state_for_prestige() -> void:
|
||||
_is_registered = false
|
||||
@@ -319,8 +393,6 @@ func reset_runtime_state_for_prestige() -> void:
|
||||
cycle_progress_seconds = 0.0
|
||||
_remaining_click_cooldown_seconds = 0.0
|
||||
_ensure_registered()
|
||||
_evaluate_generator_unlock_goal(false)
|
||||
_evaluate_buff_unlock_goals()
|
||||
visible = is_available_to_player()
|
||||
|
||||
func get_manual_click_multiplier() -> float:
|
||||
@@ -359,9 +431,30 @@ func _try_grant_click_currency() -> void:
|
||||
if _remaining_click_cooldown_seconds > 0.0:
|
||||
return
|
||||
|
||||
_add_currency(_get_click_value())
|
||||
var click_value: BigNumber = _get_click_value()
|
||||
_add_currency(click_value)
|
||||
|
||||
_remaining_click_cooldown_seconds = _get_click_cooldown_seconds()
|
||||
|
||||
# Grant research XP for manual clicks
|
||||
if data.research_data != null and click_value.mantissa > 0.0:
|
||||
if not _is_research_active(data.research_data.id):
|
||||
return
|
||||
|
||||
var research_workers: int = game_state.get_research_workers()
|
||||
if research_workers < data.research_data.min_workers_for_xp:
|
||||
return
|
||||
|
||||
var amount_float: float = click_value.mantissa * pow(10.0, float(click_value.exponent))
|
||||
var base_xp: BigNumber = BigNumber.from_float(data.research_data.xp_per_currency_produced * amount_float)
|
||||
|
||||
var worker_multiplier: float = 1.0 + (float(research_workers) * data.research_data.worker_scaling_factor)
|
||||
var scaled_xp: BigNumber = base_xp.multiply(BigNumber.from_float(worker_multiplier))
|
||||
|
||||
if game_state != null and scaled_xp.mantissa > 0.0:
|
||||
game_state.add_research_xp(data.research_data.id, scaled_xp)
|
||||
|
||||
|
||||
## Resolves click reward from data.
|
||||
func _get_click_value() -> BigNumber:
|
||||
if data == null:
|
||||
@@ -384,20 +477,10 @@ func _get_click_cooldown_seconds() -> float:
|
||||
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:
|
||||
func _get_prestige_buff_production_multiplier() -> float:
|
||||
if game_state == null:
|
||||
return 1.0
|
||||
if not manager.get_total_multiplier():
|
||||
return 1.0
|
||||
|
||||
var raw_multiplier: Variant = manager.get_total_multiplier()
|
||||
if raw_multiplier is float:
|
||||
return maxf(raw_multiplier, 0.0)
|
||||
if raw_multiplier is int:
|
||||
return maxf(float(raw_multiplier), 0.0)
|
||||
|
||||
return 1.0
|
||||
return game_state.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, _generator_id)
|
||||
|
||||
## Returns this generator's resolved state id.
|
||||
func get_generator_id() -> StringName:
|
||||
@@ -406,14 +489,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:
|
||||
@@ -425,13 +510,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:
|
||||
@@ -439,25 +528,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:
|
||||
@@ -468,29 +565,19 @@ func _float_to_big_number(value: float) -> BigNumber:
|
||||
return BigNumber.from_float(value)
|
||||
|
||||
## Converts BigNumber to float for approximate estimate math.
|
||||
func _big_number_to_float(value: BigNumber) -> float:
|
||||
if value.mantissa <= 0.0:
|
||||
return 0.0
|
||||
if value.exponent > 308:
|
||||
return INF
|
||||
if value.exponent < -308:
|
||||
return 0.0
|
||||
return value.mantissa * pow(10.0, float(value.exponent))
|
||||
|
||||
func _find_buff(buff_id: StringName) -> GeneratorBuffData:
|
||||
if data == null:
|
||||
return null
|
||||
return data.find_buff(buff_id)
|
||||
|
||||
func _resolve_buff_cost_currency_id(buff: GeneratorBuffData) -> StringName:
|
||||
if buff == null:
|
||||
return &""
|
||||
|
||||
var cost_currency: Resource = buff.cost_currency if buff.cost_currency != null else currency
|
||||
var cost_currency: Currency = buff.cost_currency if buff.cost_currency != null else currency
|
||||
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:
|
||||
@@ -502,25 +589,18 @@ func _resolve_buff_target_currency_id(buff: GeneratorBuffData) -> StringName:
|
||||
if buff == null:
|
||||
return &""
|
||||
|
||||
var target_currency: Resource = buff.resource_target_currency if buff.resource_target_currency != null else currency
|
||||
var target_currency: Currency = buff.resource_target_currency if buff.resource_target_currency != null else currency
|
||||
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:
|
||||
var multiplier: float = 1.0
|
||||
for buff in get_buffs():
|
||||
if buff == null:
|
||||
continue
|
||||
if buff.kind != kind:
|
||||
continue
|
||||
if not is_buff_unlocked(buff.id):
|
||||
continue
|
||||
|
||||
multiplier *= buff.get_effect_multiplier(get_buff_level(buff.id))
|
||||
|
||||
return maxf(multiplier, 0.0)
|
||||
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:
|
||||
@@ -550,7 +630,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
|
||||
@@ -560,8 +640,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(),
|
||||
@@ -572,6 +655,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
|
||||
@@ -581,58 +667,34 @@ func _register_buffs() -> void:
|
||||
continue
|
||||
|
||||
var buff_id: StringName = StringName(buff_id_text)
|
||||
GameState.register_generator_buff(_generator_id, buff_id, 0)
|
||||
var persisted_level: int = GameState.get_generator_buff_level(_generator_id, buff_id)
|
||||
var starts_unlocked: bool = buff.unlocked or persisted_level > 0
|
||||
GameState.register_generator_buff_unlocked(_generator_id, buff_id, starts_unlocked)
|
||||
game_state.register_buff(buff)
|
||||
|
||||
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
|
||||
game_state.set_buff_unlocked(buff_id, starts_unlocked)
|
||||
|
||||
_evaluate_buff_unlock_goals()
|
||||
|
||||
func _evaluate_buff_unlock_goals() -> void:
|
||||
for buff in get_buffs():
|
||||
_try_unlock_buff_from_goal(buff)
|
||||
|
||||
func _try_unlock_buff_from_goal(buff: GeneratorBuffData) -> void:
|
||||
if buff == null:
|
||||
return
|
||||
|
||||
var buff_id_text: String = String(buff.id).strip_edges()
|
||||
if buff_id_text.is_empty():
|
||||
return
|
||||
|
||||
var buff_id: StringName = StringName(buff_id_text)
|
||||
if GameState.is_generator_buff_unlocked(_generator_id, buff_id):
|
||||
return
|
||||
if not buff.has_unlock_goal():
|
||||
return
|
||||
if not buff.is_unlock_goal_met():
|
||||
return
|
||||
|
||||
GameState.set_generator_buff_unlocked(_generator_id, buff_id, true)
|
||||
game_state.try_unlock_buff_from_goal(buff)
|
||||
|
||||
func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void:
|
||||
_evaluate_generator_unlock_goal(false)
|
||||
_evaluate_buff_unlock_goals()
|
||||
if game_state == null:
|
||||
return
|
||||
game_state.evaluate_all_goals()
|
||||
|
||||
func try_unlock_from_goal() -> bool:
|
||||
return _evaluate_generator_unlock_goal(true)
|
||||
|
||||
func _evaluate_generator_unlock_goal(allow_manual_unlock: bool) -> bool:
|
||||
if data == null:
|
||||
return false
|
||||
func _on_goal_completed(goal_id: StringName) -> void:
|
||||
if data == null or not data.has_unlock_goal():
|
||||
return
|
||||
if data.get_unlock_goal_id() != goal_id:
|
||||
return
|
||||
if is_available_to_player():
|
||||
return false
|
||||
if not data.has_unlock_goal():
|
||||
return false
|
||||
if not data.unlocks_automatically_from_goal() and not allow_manual_unlock:
|
||||
return false
|
||||
if not data.is_unlock_goal_met():
|
||||
return false
|
||||
return
|
||||
|
||||
GameState.set_generator_unlocked(_generator_id, true)
|
||||
GameState.set_generator_available(_generator_id, true)
|
||||
goal_achieved.emit(_generator_id, data.get_unlock_goal_id())
|
||||
return 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, game_state._get_generator_state(_generator_id))
|
||||
|
||||
## Default unlocked state loaded from generator data.
|
||||
func _default_unlocked_state() -> bool:
|
||||
@@ -665,3 +727,5 @@ func _on_area_2d_mouse_exited() -> void:
|
||||
func _on_generated_state_changed(generator_id: StringName, _state: Dictionary) -> void:
|
||||
if generator_id == _generator_id:
|
||||
visible = is_available_to_player()
|
||||
if info_generator_container != null:
|
||||
info_generator_container._refresh_generator_ui()
|
||||
|
||||
Reference in New Issue
Block a user