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:
2026-05-07 20:36:21 +00:00
parent 2743fd314a
commit 81a4058b04
242 changed files with 11226 additions and 2242 deletions

View File

@@ -5,44 +5,44 @@ extends PanelContainer
@onready var _pending_value: Label = $MarginContainer/VBoxContainer/StatsGrid/PendingValue
@onready var _multiplier_value: Label = $MarginContainer/VBoxContainer/StatsGrid/MultiplierValue
@onready var _basis_label: Label = $MarginContainer/VBoxContainer/StatsGrid/BasisValue
@onready var _progress_bar: Node = $MarginContainer/VBoxContainer/PrestigeProgressBar
@onready var _reset_button: Button = $MarginContainer/VBoxContainer/Buttons/ResetButton
@onready var _save_button: Button = $MarginContainer/VBoxContainer/Buttons/SaveButton
@onready var _confirm_dialog: ConfirmationDialog = $ConfirmDialog
@onready var game_state: LevelGameState = find_parent("LevelGameState")
var _is_waiting_for_confirm: bool = false
func _ready() -> void:
var manager: Node = _get_prestige_manager()
if manager != null and manager.has_signal("prestige_state_changed"):
manager.prestige_state_changed.connect(_on_prestige_state_changed)
if manager != null and manager.has_signal("prestige_performed"):
manager.prestige_performed.connect(_on_prestige_performed)
if not GameState.currency_changed.is_connected(_on_currency_changed):
GameState.currency_changed.connect(_on_currency_changed)
game_state.ready.connect(_on_game_state_ready)
func _on_game_state_ready() -> void:
var manager: PrestigeManager = _get_prestige_manager()
manager.prestige_state_changed.connect(_on_prestige_state_changed)
manager.prestige_performed.connect(_on_prestige_performed)
game_state.currency_changed.connect(_on_currency_changed)
_refresh_ui()
func _on_reset_button_pressed() -> void:
var manager: Node = _get_prestige_manager()
if manager == null or not manager.has_method("can_prestige"):
var manager: PrestigeManager = _get_prestige_manager()
if not bool(manager.can_prestige()):
return
if not bool(manager.call("can_prestige")):
return
var pending: BigNumber = manager.call("calculate_pending_gain")
var source_currency_id: StringName = manager.call("get_source_currency_id")
var source_currency_name: String = GameState.get_currency_name(source_currency_id)
var pending: BigNumber = manager.calculate_pending_gain()
var basis_label: String = String(manager.get_basis_label())
var basis_value: BigNumber = manager.get_basis_value_for_display()
var confirm_message: String = "Reset this run for +%s prestige?\n\nThis resets currencies, owned generators, and buff levels. Lifetime totals and prestige are kept." % pending.to_string_suffix(2)
if not source_currency_name.is_empty():
confirm_message = "%s\n\nBased on %s progression." % [confirm_message, source_currency_name]
confirm_message = "%s\n\nBased on %s: %s" % [confirm_message, basis_label, basis_value.to_string_suffix(2)]
_confirm_dialog.dialog_text = confirm_message
_is_waiting_for_confirm = true
_confirm_dialog.popup_centered_ratio(0.4)
func _on_save_button_pressed() -> void:
GameState.save_game()
if game_state:
game_state.save_game()
func _on_prestige_state_changed(_total: BigNumber, _pending: BigNumber) -> void:
_refresh_ui()
@@ -58,33 +58,33 @@ func _on_confirm_dialog_confirmed() -> void:
return
_is_waiting_for_confirm = false
var manager: Node = _get_prestige_manager()
var manager: PrestigeManager = _get_prestige_manager()
if manager == null:
return
manager.call("perform_prestige")
manager.perform_prestige()
func _on_confirm_dialog_canceled() -> void:
_is_waiting_for_confirm = false
func _refresh_ui() -> void:
var manager: Node = _get_prestige_manager()
var manager: PrestigeManager = _get_prestige_manager()
if manager == null:
visible = false
return
visible = true
var total: BigNumber = manager.call("get_total_prestige")
var pending: BigNumber = manager.call("calculate_pending_gain")
var multiplier: float = float(manager.call("get_total_multiplier"))
var basis_text: String = String(manager.call("get_basis_label"))
var basis_value: BigNumber = manager.call("get_basis_value_for_display")
var total: BigNumber = manager.get_total_prestige()
var pending: BigNumber = manager.calculate_pending_gain()
var multiplier: float = float(manager.get_total_multiplier())
var basis_text: String = String(manager.get_basis_label())
var basis_value: BigNumber = manager.get_basis_value_for_display()
_total_value.text = total.to_string_suffix(2)
_pending_value.text = pending.to_string_suffix(2)
_multiplier_value.text = "x%.2f" % multiplier
_basis_label.text = "%s (%s)" % [basis_text, basis_value.to_string_suffix(2)]
_reset_button.disabled = not bool(manager.call("can_prestige"))
_reset_button.disabled = not bool(manager.can_prestige())
func _get_prestige_manager() -> Node:
return get_node_or_null("/root/PrestigeManager")
func _get_prestige_manager() -> PrestigeManager:
return game_state.prestige_manager