diff --git a/core/generator/currency_generator.gd b/core/generator/currency_generator.gd index f914ce3..3217597 100644 --- a/core/generator/currency_generator.gd +++ b/core/generator/currency_generator.gd @@ -148,10 +148,18 @@ func _grant_cycle_income(cycle_count: int) -> void: 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 research_xp_amount: BigNumber = BigNumber.from_float(data.research_data.xp_per_currency_produced * amount_float) - if game_state != null and research_xp_amount.mantissa > 0.0: - game_state.add_research_xp(data.research_data.id, research_xp_amount) + 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: @@ -433,10 +441,18 @@ func _try_grant_click_currency() -> void: 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 research_xp_amount: BigNumber = BigNumber.from_float(data.research_data.xp_per_currency_produced * amount_float) - if game_state != null and research_xp_amount.mantissa > 0.0: - game_state.add_research_xp(data.research_data.id, research_xp_amount) + 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. diff --git a/core/level_game_state.gd b/core/level_game_state.gd index b837b62..acf3502 100644 --- a/core/level_game_state.gd +++ b/core/level_game_state.gd @@ -23,6 +23,10 @@ signal goal_progress_changed(goal_id: StringName, progress: float) signal research_xp_changed(research_id: StringName, new_xp: BigNumber) ## Emitted when a research track levels up. signal research_level_up(research_id: StringName, old_level: int, new_level: int) +## Emitted when worker count changes (derived from currency balance). +signal worker_count_changed(currency_id: StringName, new_count: int) +## Emitted when research workers change. +signal research_workers_changed(new_count: int) # ============================================================================== # EXPORTED REFERENCES @@ -72,6 +76,8 @@ var _goal_completed: Dictionary = {} var research_xp: Dictionary = {} ## Research levels by research ID. var research_levels: Dictionary = {} +## Workers assigned to research (total across all tracks). +var research_workers: int = 0 ## Unix timestamp of last save. var last_save_time: int = 0 @@ -81,8 +87,8 @@ var last_save_time: int = 0 # ============================================================================== ## JSON key for save format version. const SAVE_FORMAT_VERSION_KEY: String = "save_format_version" -## Current save format version (5 includes research XP BigNumber serialization). -const CURRENT_SAVE_FORMAT_VERSION: int = 5 +## Current save format version (7 includes research workers tracking). +const CURRENT_SAVE_FORMAT_VERSION: int = 7 ## JSON key for generator owned count. const GENERATOR_OWNED_KEY: String = "owned" ## JSON key for generator purchased count. @@ -120,6 +126,8 @@ const LAST_SAVE_TIME_KEY: String = "last_save_time" const RESEARCH_XP_KEY: String = "research_xp" ## JSON key for research levels section. const RESEARCH_LEVELS_KEY: String = "research_levels" +## JSON key for research workers section. +const RESEARCH_WORKERS_KEY: String = "research_workers" ## Reference to the PrestigeManager autoload node (set in _ready). var prestige_manager: PrestigeManager @@ -484,6 +492,31 @@ func get_research_multiplier(research_id: StringName) -> float: return 1.0 return research.get_multiplier_for_level(level) +## Returns the total number of workers assigned to research. +func get_research_workers() -> int: + return research_workers + +## Assigns a worker to research (called when spending worker currency on research). +func assign_worker_to_research() -> bool: + var worker_currency: BigNumber = get_currency_amount_by_id("worker") + if worker_currency.mantissa < 1.0: + return false + + spend_currency_by_id("worker", BigNumber.from_float(1.0)) + research_workers += 1 + research_workers_changed.emit(research_workers) + return true + +## Removes a worker from research (returns to worker currency). +func remove_worker_from_research() -> bool: + if research_workers <= 0: + return false + + research_workers -= 1 + add_currency_by_id("worker", BigNumber.from_float(1.0)) + research_workers_changed.emit(research_workers) + return true + ## Adds XP to a research track, applying buff modifiers and emitting signals on level up. func add_research_xp(research_id: StringName, xp: BigNumber) -> void: var actual_xp: BigNumber = ResearchBuffCalculator.apply_buffs(self, research_id, xp) @@ -508,6 +541,11 @@ func _get_research_data(research_id: StringName) -> ResearchData: return null return research_catalogue.get_research_by_id(research_id) +## Returns the current worker count (derived from "worker" currency balance). +func get_worker_count() -> int: + var worker_amount: BigNumber = get_currency_amount_by_id("worker") + return int(worker_amount.mantissa) if worker_amount.mantissa >= 0 else 0 + #endregion # ============================================================================== @@ -588,6 +626,7 @@ func save_game() -> void: GOALS_KEY: _serialize_goals(), RESEARCH_XP_KEY: _serialize_research_xp(), RESEARCH_LEVELS_KEY: research_levels.duplicate(), + RESEARCH_WORKERS_KEY: research_workers, LAST_SAVE_TIME_KEY: Time.get_unix_time_from_system() } @@ -639,13 +678,15 @@ func load_game() -> void: research_xp = _deserialize_research_xp(parsed_data.get(RESEARCH_XP_KEY, {})) if parsed_data.has(RESEARCH_LEVELS_KEY): research_levels = parsed_data.get(RESEARCH_LEVELS_KEY, {}) + if parsed_data.has(RESEARCH_WORKERS_KEY): + research_workers = int(parsed_data.get(RESEARCH_WORKERS_KEY, 0)) last_save_time = parsed_data.get(LAST_SAVE_TIME_KEY, Time.get_unix_time_from_system()) for save_key_variant in parsed_data.keys(): var save_key: String = String(save_key_variant) if save_key.is_empty(): continue - if save_key in [SAVE_FORMAT_VERSION_KEY, CURRENCIES_KEY, GENERATOR_STATES_KEY, BUFF_LEVELS_KEY, BUFF_UNLOCKED_KEY, BUFF_ACTIVE_KEY, GOALS_KEY, RESEARCH_XP_KEY, RESEARCH_LEVELS_KEY, LAST_SAVE_TIME_KEY]: + if save_key in [SAVE_FORMAT_VERSION_KEY, CURRENCIES_KEY, GENERATOR_STATES_KEY, BUFF_LEVELS_KEY, BUFF_UNLOCKED_KEY, BUFF_ACTIVE_KEY, GOALS_KEY, RESEARCH_XP_KEY, RESEARCH_LEVELS_KEY, RESEARCH_WORKERS_KEY, LAST_SAVE_TIME_KEY]: continue var section_payload: Variant = parsed_data.get(save_key_variant) diff --git a/core/research/research_data.gd b/core/research/research_data.gd index dc28367..1b5f179 100644 --- a/core/research/research_data.gd +++ b/core/research/research_data.gd @@ -19,6 +19,10 @@ extends Resource ## Associated Buff (single buff per research track) @export var associated_buff_id: StringName = &"" +## Worker Scaling Configuration +@export var worker_scaling_factor: float = 0.01 +@export var min_workers_for_xp: int = 1 + func get_xp_required_for_level(level: int) -> float: return base_xp_required * pow(xp_growth_multiplier, float(maxi(level - 1, 0))) diff --git a/core/research/research_panel.gd b/core/research/research_panel.gd index b4258bc..81e14cd 100644 --- a/core/research/research_panel.gd +++ b/core/research/research_panel.gd @@ -5,6 +5,7 @@ const RESEARCH_ROW_SCENE: PackedScene = preload("res://core/research/research_ro @onready var _title_label: Label = $ScrollContainer/VBoxContainer/TitleLabel @onready var _research_rows_container: VBoxContainer = $ScrollContainer/VBoxContainer/ResearchRows +@onready var _buy_worker_button: Button = $ScrollContainer/VBoxContainer/HBoxContainer/BuyWorkerButton var _game_state: LevelGameState var _research_rows: Dictionary = {} @@ -18,9 +19,12 @@ func _ready() -> void: _game_state.research_xp_changed.connect(_on_research_xp_changed) _game_state.research_level_up.connect(_on_research_level_up) + _game_state.currency_changed.connect(_on_currency_changed) + _game_state.research_workers_changed.connect(_on_research_workers_changed) _build_research_rows() _refresh_all() + _refresh_worker_button() func _build_research_rows() -> void: for child in _research_rows_container.get_children(): @@ -109,3 +113,43 @@ func activate_research(research_id: StringName) -> void: if _research_rows.has(research_id): var row: ResearchRow = _research_rows[research_id] row.set_active(true) + +func _on_currency_changed(currency_id: StringName, _new_amount: BigNumber) -> void: + if currency_id == &"worker": + _refresh_worker_button() + +func _on_research_workers_changed(_new_count: int) -> void: + _refresh_worker_button() + +func _can_buy_worker() -> bool: + if _game_state == null: + return false + + var worker_currency: BigNumber = _game_state.get_currency_amount_by_id(&"worker") + + return worker_currency.mantissa >= 1.0 + +func _buy_worker() -> void: + if _game_state == null: + return + + _game_state.assign_worker_to_research() + +func _on_buy_worker_pressed() -> void: + _buy_worker() + _refresh_worker_button() + +func _refresh_worker_button() -> void: + if _buy_worker_button == null: + return + + if _game_state == null: + _buy_worker_button.disabled = true + return + + var can_buy: bool = _can_buy_worker() + _buy_worker_button.disabled = not can_buy + + var worker_currency: BigNumber = _game_state.get_currency_amount_by_id(&"worker") + var research_workers: int = _game_state.get_research_workers() + _buy_worker_button.text = "Assign Worker to Research (%d available, %d assigned)" % [int(worker_currency.mantissa), research_workers] diff --git a/core/research/research_panel.tscn b/core/research/research_panel.tscn index eb6525c..0ee16e8 100644 --- a/core/research/research_panel.tscn +++ b/core/research/research_panel.tscn @@ -1,26 +1,34 @@ -[gd_scene format=3 uid="uid://dd8roif0mqirl"] +[gd_scene format=3 uid="uid://6d101h70mcx"] [ext_resource type="Script" uid="uid://fdl7ftxw8w1e" path="res://core/research/research_panel.gd" id="1_script"] -[ext_resource type="PackedScene" uid="uid://bpo8pl5tipav" path="res://core/research/research_row.tscn" id="2_3m5ee"] +[ext_resource type="PackedScene" uid="uid://ckr805xqy6h4w" path="res://core/research/worker_summary_label.tscn" id="3_worker"] -[node name="ResearchPanel" type="PanelContainer" unique_id=1105274967] +[node name="ResearchPanel" type="PanelContainer" unique_id=600021293] custom_minimum_size = Vector2(400, 100) offset_right = 400.0 offset_bottom = 100.0 script = ExtResource("1_script") -[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=1099596602] +[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=973132184] layout_mode = 2 -[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer" unique_id=692886617] +[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer" unique_id=1679714014] layout_mode = 2 -[node name="TitleLabel" type="Label" parent="ScrollContainer/VBoxContainer" unique_id=1565843659] +[node name="TitleLabel" type="Label" parent="ScrollContainer/VBoxContainer" unique_id=990297493] layout_mode = 2 text = "Research" -[node name="ResearchRows" type="VBoxContainer" parent="ScrollContainer/VBoxContainer" unique_id=1200240263] +[node name="HBoxContainer" type="HBoxContainer" parent="ScrollContainer/VBoxContainer" unique_id=1419318708] layout_mode = 2 -[node name="ResearchRow" parent="ScrollContainer/VBoxContainer/ResearchRows" unique_id=245375198 instance=ExtResource("2_3m5ee")] +[node name="WorkerSummaryLabel" parent="ScrollContainer/VBoxContainer/HBoxContainer" unique_id=13607753 instance=ExtResource("3_worker")] + +[node name="BuyWorkerButton" type="Button" parent="ScrollContainer/VBoxContainer/HBoxContainer" unique_id=1083899286] layout_mode = 2 +text = "Invest Worker (0 available)" + +[node name="ResearchRows" type="VBoxContainer" parent="ScrollContainer/VBoxContainer" unique_id=2066050169] +layout_mode = 2 + +[connection signal="pressed" from="ScrollContainer/VBoxContainer/HBoxContainer/BuyWorkerButton" to="." method="_buy_worker"] diff --git a/core/research/worker_summary_label.gd b/core/research/worker_summary_label.gd new file mode 100644 index 0000000..26ce4f2 --- /dev/null +++ b/core/research/worker_summary_label.gd @@ -0,0 +1,27 @@ +class_name WorkerSummaryLabel +extends Label + +@onready var _game_state: LevelGameState = find_parent("LevelGameState") + +signal research_workers_changed(new_count: int) + +func _ready() -> void: + if _game_state == null: + push_error("WorkerSummaryLabel: Could not find LevelGameState parent") + return + + _game_state.currency_changed.connect(_on_currency_changed) + _game_state.research_workers_changed.connect(_on_research_workers_changed) + _update_worker_display() + +func _on_currency_changed(currency_id: StringName, _new_amount: BigNumber) -> void: + if currency_id == "worker": + _update_worker_display() + +func _on_research_workers_changed(new_count: int) -> void: + _update_worker_display() + +func _update_worker_display() -> void: + var worker_currency: int = int(_game_state.get_currency_amount_by_id("worker").mantissa) + var research_workers: int = _game_state.get_research_workers() + text = "Workers: %d available, %d assigned" % [worker_currency, research_workers] diff --git a/core/research/worker_summary_label.gd.uid b/core/research/worker_summary_label.gd.uid new file mode 100644 index 0000000..3b68f1d --- /dev/null +++ b/core/research/worker_summary_label.gd.uid @@ -0,0 +1 @@ +uid://brvoy607b3nft diff --git a/core/research/worker_summary_label.tscn b/core/research/worker_summary_label.tscn new file mode 100644 index 0000000..1a36862 --- /dev/null +++ b/core/research/worker_summary_label.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3 uid="uid://ckr8z5xqy6h4w"] + +[ext_resource type="Script" path="res://core/research/worker_summary_label.gd" id="1_script"] + +[node name="WorkerSummaryLabel" type="Label"] +layout_mode = 2 +script = ExtResource("1_script") +text = "Workers: 0"