Buy Max Manager
Relevant source files
Purpose and Scope
Section titled “Purpose and Scope”The Buy Max Manager is a utility component that adds automated batch purchasing functionality to the game’s upgrade system. It injects a split button into the upgrades tab UI, providing players with four different purchase strategies to efficiently spend accumulated currency on upgrades. This manager is part of the optional gameplay enhancements and is always enabled once the mod is loaded.
For information about other utility managers (FocusHandler, WireColorOverrides, etc.), see Other Utility Managers. For the broader context of gameplay features including cheats and node limit controls, see Gameplay Features. For configuration management details, see Configuration System.
Sources: extensions/scripts/utilities/buy_max_manager.gd L1-L10
Architecture Overview
Section titled “Architecture Overview”Component Lifecycle
Section titled “Component Lifecycle”The BuyMaxManager extends Node and follows the standard mod manager initialization pattern. It is instantiated and configured by mod_main.gd during the deferred setup phase after the HUD is available.
sequenceDiagram
participant mod_main.gd
participant BuyMaxManager
participant ConfigManager
participant Main/HUD
participant UpgradesTab UI
mod_main.gd->>BuyMaxManager: "new()"
mod_main.gd->>BuyMaxManager: "setup(tree, config)"
BuyMaxManager->>ConfigManager: "get_value('buy_max_strategy')"
ConfigManager-->>BuyMaxManager: "Strategy.ROUND_ROBIN"
BuyMaxManager->>Main/HUD: "_find_upgrades_tab()"
Main/HUD-->>BuyMaxManager: "VBoxContainer reference"
BuyMaxManager->>BuyMaxManager: "_inject_buy_max_button()"
BuyMaxManager->>UpgradesTab UI: "add BuyMaxContainer"
BuyMaxManager->>UpgradesTab UI: "add BuyMaxButton"
BuyMaxManager->>UpgradesTab UI: "add StrategyButton"
BuyMaxManager->>BuyMaxManager: "set _initialized = true"
note over BuyMaxManager,UpgradesTab UI: "Ready for user interaction"
Key Initialization Steps:
- Load saved strategy from
ConfigManager - Locate the upgrades tab in the HUD scene tree
- Inject UI components into the buttons panel
- Configure event handlers and styling
Sources: extensions/scripts/utilities/buy_max_manager.gd L45-L76
Purchase Strategy System
Section titled “Purchase Strategy System”Strategy Enumeration
Section titled “Strategy Enumeration”The manager implements four distinct purchase strategies defined in the Strategy enum:
| Strategy ID | Name | Behavior | Use Case |
|---|---|---|---|
ROUND_ROBIN (0) | Round Robin | Buys 1 level of each upgrade in rotation until funds exhausted | Balanced progression across all upgrades |
CHEAPEST_FIRST (1) | Cheapest First | Always purchases the cheapest available upgrade first | Maximize number of purchases, incremental gains |
EXPENSIVE_FIRST (2) | Most Expensive | Buys the most expensive affordable upgrade | Maximize impact per purchase |
TOP_TO_BOTTOM (3) | Top to Bottom | Maxes out upgrades sequentially from top to bottom of UI | Complete individual upgrades before moving to next |
Sources: extensions/scripts/utilities/buy_max_manager.gd L12-L31
Strategy Selection Flow
Section titled “Strategy Selection Flow”flowchart TD User["👤 User"] StratBtn["StrategyButton (MenuButton)"] Popup["PopupMenu"] Handler["_on_strategy_selected(id)"] SetStrat["set_strategy(strategy)"] Config["ConfigManager"] Update["_update_strategy_menu()"] Tooltip["_update_main_button_tooltip()"] Signal["Signals.notify.emit()"] User --> StratBtn StratBtn --> Popup Popup --> Handler Handler --> SetStrat SetStrat --> Config SetStrat --> Update Update --> Tooltip SetStrat --> Signal Update --> Popup
Strategy Persistence: The selected strategy is immediately persisted via ConfigManager.set_value("buy_max_strategy", strategy) and restored on next game session.
Sources: extensions/scripts/utilities/buy_max_manager.gd L79-L87
extensions/scripts/utilities/buy_max_manager.gd L238-L242
UI Injection System
Section titled “UI Injection System”Split Button Architecture
Section titled “Split Button Architecture”The Buy Max button uses a split-button pattern consisting of two components inside an HBoxContainer:
flowchart TD
ExistingBtns["Existing Tab Buttons"]
MainBtn["BuyMaxButton<br>text: 'Buy Max'<br>theme: TabButton<br>SIZE_FILL"]
DropBtn["StrategyButton<br>text: '▼'<br>theme: TabButton<br>min_size: 45x0"]
ExecuteBuyMax["_execute_buy_max()"]
PopupMenu["PopupMenu (4 items)"]
MainBtn --> ExecuteBuyMax
DropBtn --> PopupMenu
subgraph ButtonsPanel/ButtonsContainer ["ButtonsPanel/ButtonsContainer"]
ExistingBtns
subgraph BuyMaxCont ["BuyMaxCont"]
MainBtn
DropBtn
end
end
UI Tree Location: The manager searches for a VBoxContainer that contains both ButtonsPanel/ButtonsContainer and TabContainer children, which uniquely identifies the upgrades tab structure.
Theme Matching: Both buttons use theme_type_variation = "TabButton" to match the game’s existing tab buttons. The popup menu is styled with StyleBoxFlat to match the dark theme defined in main.tres.
Sources: extensions/scripts/utilities/buy_max_manager.gd L112-L169
extensions/scripts/utilities/buy_max_manager.gd L202-L235
Upgrades Tab Discovery
Section titled “Upgrades Tab Discovery”flowchart TD HUD["Main/HUD"] Search["_find_upgrades_tab(node)"] Check["Is VBoxContainer?"] CheckBtns["Has ButtonsPanel/ButtonsContainer?"] CheckTabs["Has TabContainer?"] Found["Return VBoxContainer"] Recurse["Recurse children"] HUD --> Search Search --> Check Check --> CheckBtns Check --> Recurse CheckBtns --> CheckTabs CheckBtns --> Recurse CheckTabs --> Found CheckTabs --> Recurse Recurse --> Search
Sources: extensions/scripts/utilities/buy_max_manager.gd L94-L108
Purchase Algorithm Details
Section titled “Purchase Algorithm Details”Main Execution Flow
Section titled “Main Execution Flow”flowchart TD
Trigger["BuyMaxButton.pressed"]
Execute["_execute_buy_max()"]
Sound["Sound.play('click_toggle2')"]
DoBuyMax["_do_buy_max() -> int"]
GetPanels["_get_active_upgrade_panels()"]
Filter["Filter out token upgrades"]
Dispatch["Strategy?"]
RR["_buy_round_robin()"]
CF["_buy_cheapest_first()"]
EF["_buy_expensive_first()"]
TB["_buy_top_to_bottom()"]
Result["Return total purchased"]
Notify["Signals.notify.emit()"]
Trigger --> Execute
Execute --> Sound
Execute --> DoBuyMax
DoBuyMax --> GetPanels
GetPanels --> Filter
Filter --> Dispatch
Dispatch --> RR
Dispatch --> CF
Dispatch --> EF
Dispatch --> TB
RR --> Result
CF --> Result
EF --> Result
TB --> Result
Result --> Notify
Panel Discovery: The algorithm queries the currently active tab in the TabContainer and recursively searches for all visible Panel nodes that have both can_purchase() and update_all() methods.
Token Exclusion: Upgrades with currency_key == "currency:token" are filtered out before processing to prevent accidental token spending.
Sources: extensions/scripts/utilities/buy_max_manager.gd L244-L283
extensions/scripts/utilities/buy_max_manager.gd L405-L432
Strategy Implementation Details
Section titled “Strategy Implementation Details”Round Robin Strategy
Section titled “Round Robin Strategy”Groups upgrades by currency type and rotates through each group, buying one level at a time until no more purchases are possible in that currency.
Algorithm:
1. Group panels by _get_currency_key(panel)2. For each currency group: a. While any purchase made this pass: - For each panel in group: * panel.update_all() * If can_purchase(): panel._on_purchase_pressed() b. Final pass: attempt one more purchase per panel3. Return total purchasedSources: extensions/scripts/utilities/buy_max_manager.gd L290-L323
Cheapest First Strategy
Section titled “Cheapest First Strategy”Finds and purchases the globally cheapest affordable upgrade, repeating until no more purchases are possible.
Algorithm:
1. While any purchase made: a. Set cheapest_cost = INF, cheapest_panel = null b. For each panel: - panel.update_all() - If can_purchase() and cost < cheapest_cost: * Update cheapest_cost and cheapest_panel c. If cheapest_panel found: - cheapest_panel._on_purchase_pressed() - Increment total2. Return total purchasedSources: extensions/scripts/utilities/buy_max_manager.gd L326-L351
Most Expensive First Strategy
Section titled “Most Expensive First Strategy”Purchases the most expensive affordable upgrade repeatedly, maximizing impact per transaction.
Algorithm:
1. While any purchase made: a. Set highest_cost = -1, expensive_panel = null b. For each panel: - panel.update_all() - If can_purchase() and cost > highest_cost: * Update highest_cost and expensive_panel c. If expensive_panel found: - expensive_panel._on_purchase_pressed() - Increment total2. Return total purchasedSources: extensions/scripts/utilities/buy_max_manager.gd L354-L379
Top to Bottom Strategy
Section titled “Top to Bottom Strategy”Processes panels in UI order (top to bottom), exhausting each upgrade before moving to the next.
Algorithm:
1. For each panel in order: a. While panel.can_purchase(): - panel.update_all() - panel._on_purchase_pressed() - Increment total2. Return total purchasedSources: extensions/scripts/utilities/buy_max_manager.gd L382-L398
Currency Grouping System
Section titled “Currency Grouping System”The _get_currency_key() helper function groups upgrades by their cost type for fair distribution in Round Robin strategy:
flowchart TD Panel["upgrade_panel"] GetKey["_get_currency_key(panel)"] CheckData["Panel in Data.upgrades?"] GetCostType["cost_type = upgrade_data.cost_type"] CheckType["Cost Type?"] Incremental["INCREMENTAL/FIXED"] Attribute["ATTRIBUTE"] Unknown["'unknown'"] CurrencyKey["'currency:' + currency"] AttributeKey["'attribute:' + attribute_cost"] Panel --> GetKey GetKey --> CheckData CheckData --> Unknown CheckData --> GetCostType GetCostType --> CheckType CheckType --> Incremental CheckType --> Attribute CheckType --> Unknown Incremental --> CurrencyKey Attribute --> AttributeKey
Currency Key Examples:
"currency:dollar"- Standard dollar upgrades"currency:research"- Research point upgrades"attribute:compute"- Upgrades costing compute attribute"currency:token"- Token upgrades (filtered out)
Sources: extensions/scripts/utilities/buy_max_manager.gd L435-L450
Configuration and Persistence
Section titled “Configuration and Persistence”Persisted State
Section titled “Persisted State”| Configuration Key | Type | Default | Description |
|---|---|---|---|
buy_max_strategy | int | Strategy.ROUND_ROBIN (0) | Currently selected purchase strategy |
The strategy preference is:
- Loaded in
setup()viaconfig.get_value("buy_max_strategy", Strategy.ROUND_ROBIN) - Saved immediately on change via
config.set_value("buy_max_strategy", strategy) - Persisted to
user://tajs_mod_config.json
Configuration Flow:
flowchart TD Load["setup() loads config"] Mem["current_strategy variable"] UI["StrategyButton dropdown"] User["User selection"] Save["set_strategy() saves"] Config["tajs_mod_config.json"] Load --> Mem Mem --> UI User --> UI UI --> Save Save --> Config Config --> Load
Sources: extensions/scripts/utilities/buy_max_manager.gd L52-L54
extensions/scripts/utilities/buy_max_manager.gd L80-L86
Integration with Game Systems
Section titled “Integration with Game Systems”Upgrade Panel Interface
Section titled “Upgrade Panel Interface”The Buy Max Manager interacts with the game’s upgrade panels through their public interface:
| Method | Purpose | Called By |
|---|---|---|
update_all() | Recalculates cost and affordability | Before each can_purchase() check |
can_purchase() | Returns bool if upgrade is affordable | Decision logic in all strategies |
_on_purchase_pressed() | Executes the purchase transaction | After affordability confirmed |
Panel Properties:
panel.cost- Current cost of next upgrade levelpanel.visible- Whether panel is shown (used for filtering)panel.name- Upgrade identifier, maps toData.upgrades[name]
Sources: extensions/scripts/utilities/buy_max_manager.gd L309-L321
extensions/scripts/utilities/buy_max_manager.gd L426-L432
Signal Integration
Section titled “Signal Integration”flowchart TD BMM["BuyMaxManager"] Sound["Sound.play()"] Signals["Signals.notify.emit()"] NotifLog["Notification Log"] BMM --> Sound BMM --> Signals BMM --> Signals Signals --> NotifLog
Notification Messages:
- Success:
"Bought %d upgrades (%s)"with strategy name - Failure:
"Nothing affordable on this page" - Strategy change:
"Strategy: %s"with strategy name
Sources: extensions/scripts/utilities/buy_max_manager.gd L246-L252
extensions/scripts/utilities/buy_max_manager.gd L85
Error Handling and Edge Cases
Section titled “Error Handling and Edge Cases”Initialization Guards
Section titled “Initialization Guards”The manager includes defensive checks to prevent initialization failures:
flowchart TD Setup["setup(tree, config)"] Check1["_initialized?"] Check2["Main exists?"] Check3["HUD exists?"] Check4["_upgrades_tab found?"] Success["Inject button & set _initialized"] EarlyReturn["Log warning & return"] Setup --> Check1 Check1 --> EarlyReturn Check1 --> Check2 Check2 --> EarlyReturn Check2 --> Check3 Check3 --> EarlyReturn Check3 --> Check4 Check4 --> EarlyReturn Check4 --> Success
Duplicate Injection Prevention
Section titled “Duplicate Injection Prevention”Before creating UI components, the manager checks if BuyMaxContainer already exists in the button panel using buttons_container.has_node("BuyMaxContainer"). If found, it reuses the existing reference rather than creating duplicates.
Sources: extensions/scripts/utilities/buy_max_manager.gd L46-L76
extensions/scripts/utilities/buy_max_manager.gd L118-L121
Empty Panel Handling
Section titled “Empty Panel Handling”flowchart TD DoBuyMax["_do_buy_max()"] GetPanels["_get_active_upgrade_panels()"] CheckEmpty1["panels.is_empty()?"] Filter["Filter out tokens"] CheckEmpty2["filtered.is_empty()?"] Execute["Execute strategy"] Return0["return 0"] DoBuyMax --> GetPanels GetPanels --> CheckEmpty1 CheckEmpty1 --> Return0 CheckEmpty1 --> Filter Filter --> CheckEmpty2 CheckEmpty2 --> Return0 CheckEmpty2 --> Execute
Sources: extensions/scripts/utilities/buy_max_manager.gd L257-L271
Cross-References
Section titled “Cross-References”This manager is referenced by other mod components:
- mod_main.gd: Instantiates and calls
setup()during_setup_for_main() - default_commands.gd: Potentially includes commands to trigger buy max or change strategy
- Settings Panel: May include UI controls to disable or configure the feature
The manager operates independently once initialized and requires no ongoing coordination with mod_main.gd beyond the initial setup phase.
Sources: High-level architecture context from provided diagrams