diff --git a/core/currency/README.md b/core/currency/README.md index 6737f8d..58e58ab 100644 --- a/core/currency/README.md +++ b/core/currency/README.md @@ -108,4 +108,4 @@ The `CurrencyCatalogue` is a resource that holds all defined currencies and is a -/home/mikymod/work/jmp/idle/core/currency/README.md \ No newline at end of file +/home/mikymod/work/jmp/idle/core/currency/README.md diff --git a/core/generator/currency_generator.gd b/core/generator/currency_generator.gd index 6322da9..feb8c55 100644 --- a/core/generator/currency_generator.gd +++ b/core/generator/currency_generator.gd @@ -149,12 +149,17 @@ func _grant_cycle_income(cycle_count: int) -> void: 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 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: var tracker: ResearchXPTracker = game_state.research_tracker as ResearchXPTracker if tracker: tracker.add_xp_with_buffs(data.research_data.id, research_xp_amount) + else: + game_state.add_research_xp(data.research_data.id, research_xp_amount) ## Convenience helper for the next single-unit purchase cost. func get_next_cost() -> BigNumber: @@ -359,6 +364,29 @@ func get_research_multiplier() -> float: 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 _is_registering = false @@ -405,17 +433,23 @@ func _try_grant_click_currency() -> void: 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 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: var tracker: ResearchXPTracker = game_state.research_tracker as ResearchXPTracker if tracker: tracker.add_xp_with_buffs(data.research_data.id, research_xp_amount) + else: + game_state.add_research_xp(data.research_data.id, research_xp_amount) - _remaining_click_cooldown_seconds = _get_click_cooldown_seconds() ## Resolves click reward from data. func _get_click_value() -> BigNumber: diff --git a/core/level_game_state.gd b/core/level_game_state.gd index 941435a..dc2aba2 100644 --- a/core/level_game_state.gd +++ b/core/level_game_state.gd @@ -440,14 +440,6 @@ func get_effective_multiplier(generator_id: StringName, kind: int) -> float: return multiplier func _initialize_research() -> void: - research_tracker = Node.new() - research_tracker.set_script(load("res://core/research/research_xp_tracker.gd")) - add_child(research_tracker) - - var tracker: ResearchXPTracker = research_tracker as ResearchXPTracker - if tracker: - tracker.game_state = self - if research_catalogue: for research in research_catalogue.get_all_research(): register_research(research.id) diff --git a/core/research/README.md b/core/research/README.md index 7b1cbb8..5dd6b25 100644 --- a/core/research/README.md +++ b/core/research/README.md @@ -2,7 +2,9 @@ ## Overview -The `research/` subfolder implements a production-based research system where generator output earns research XP, automatically leveling up to provide production multipliers. Research tracks are tied to specific generators and can be enhanced with purchasable buffs that increase XP gain. +The `research/` subfolder implements a production-based research system where generator output earns +research XP, automatically leveling up to provide production multipliers. Research tracks are tied to +specific generators and can be enhanced with purchasable buffs that increase XP gain. ## Files diff --git a/core/research/research_panel.gd b/core/research/research_panel.gd index 9c9b9dd..b4258bc 100644 --- a/core/research/research_panel.gd +++ b/core/research/research_panel.gd @@ -8,6 +8,7 @@ const RESEARCH_ROW_SCENE: PackedScene = preload("res://core/research/research_ro var _game_state: LevelGameState var _research_rows: Dictionary = {} +var _active_research_id: StringName = &"" func _ready() -> void: _game_state = find_parent("LevelGameState") @@ -25,13 +26,17 @@ func _build_research_rows() -> void: for child in _research_rows_container.get_children(): child.queue_free() _research_rows.clear() + _active_research_id = &"" if _game_state.research_catalogue: for research in _game_state.research_catalogue.get_all_research(): var row: ResearchRow = RESEARCH_ROW_SCENE.instantiate() _research_rows_container.add_child(row) row.setup(research) + row.active_changed.connect(_on_research_active_changed.bind(research.id)) _research_rows[research.id] = row + + _load_active_research_state() func _refresh_all() -> void: for row in _research_rows.values(): @@ -58,3 +63,49 @@ func _on_research_xp_changed(research_id: StringName, _new_xp: BigNumber) -> voi func _on_research_level_up(research_id: StringName, _old_level: int, _new_level: int) -> void: if _research_rows.has(research_id): _refresh_row(_research_rows[research_id]) + +func _on_research_active_changed(is_active: bool, research_id: StringName) -> void: + if is_active: + if _active_research_id != &"" and _active_research_id != research_id: + var existing_row: ResearchRow = _research_rows.get(_active_research_id) + if existing_row != null: + existing_row.set_active(false, false) + _active_research_id = research_id + _save_active_research_state() + else: + if _active_research_id == research_id: + _active_research_id = &"" + _save_active_research_state() + +func _load_active_research_state() -> void: + if _game_state == null: + return + + var save_data: Dictionary = _game_state.get_external_save_data("research_active") + if save_data.has("active_research_id"): + var loaded_id: String = String(save_data.get("active_research_id", "")) + if not loaded_id.is_empty(): + _active_research_id = StringName(loaded_id) + var row: ResearchRow = _research_rows.get(_active_research_id) + if row != null: + row.set_active(true, false) + +func _save_active_research_state() -> void: + if _game_state == null: + return + + var save_data: Dictionary = {} + if _active_research_id != &"": + save_data["active_research_id"] = String(_active_research_id) + _game_state.set_external_save_data("research_active", save_data) + +func get_active_research_id() -> StringName: + return _active_research_id + +func is_research_active(research_id: StringName) -> bool: + return _active_research_id == research_id + +func activate_research(research_id: StringName) -> void: + if _research_rows.has(research_id): + var row: ResearchRow = _research_rows[research_id] + row.set_active(true) diff --git a/core/research/research_row.gd b/core/research/research_row.gd index 0daec0f..3519e41 100644 --- a/core/research/research_row.gd +++ b/core/research/research_row.gd @@ -1,14 +1,18 @@ class_name ResearchRow extends HBoxContainer +signal active_changed(is_active: bool) + @onready var _name_label: Label = $VBoxContainer/NameLabel @onready var _level_label: Label = $VBoxContainer/HBoxContainer/LevelLabel @onready var _progress_bar: ProgressBar = $VBoxContainer/HBoxContainer/ProgressBar @onready var _multiplier_label: Label = $VBoxContainer/HBoxContainer/MultiplierLabel @onready var _buff_label: Label = $VBoxContainer/HBoxContainer/BuffLabel +@onready var _check_button: CheckButton = $CheckButton var _research: ResearchData var _game_state: LevelGameState +var _is_active: bool = false func _ready() -> void: _game_state = find_parent("LevelGameState") @@ -18,6 +22,7 @@ func _ready() -> void: _game_state.research_xp_changed.connect(_on_research_xp_changed) _game_state.research_level_up.connect(_on_research_level_up) + _check_button.toggled.connect(_on_check_button_toggled) func setup(research: ResearchData) -> void: _research = research @@ -72,3 +77,22 @@ func _update_from_state() -> void: var xp_needed: BigNumber = _research.get_xp_required_for_level_big(level + 1) update_display(level, progress, multiplier, xp_current, xp_needed) + +func _on_check_button_toggled(toggled_on: bool) -> void: + if toggled_on != _is_active: + _is_active = toggled_on + active_changed.emit(_is_active) + +func set_active(is_active: bool, emit_signal: bool = true) -> void: + _is_active = is_active + _check_button.button_pressed = is_active + if emit_signal: + active_changed.emit(is_active) + +func is_research_active() -> bool: + return _is_active + +func get_research_id_public() -> StringName: + if _research == null: + return &"" + return _research.id diff --git a/core/research/research_row.tscn b/core/research/research_row.tscn index c743183..78da542 100644 --- a/core/research/research_row.tscn +++ b/core/research/research_row.tscn @@ -36,3 +36,7 @@ text = "+0%" [node name="BuffLabel" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=2124734446] layout_mode = 2 + +[node name="CheckButton" type="CheckButton" parent="." unique_id=3456789012] +layout_mode = 2 +text = "Active" diff --git a/core/research/research_xp_tracker.gd b/core/research/research_xp_tracker.gd index 950de72..d1eb22a 100644 --- a/core/research/research_xp_tracker.gd +++ b/core/research/research_xp_tracker.gd @@ -31,6 +31,9 @@ func _on_generator_produced(generator_id: StringName, _amount: Variant) -> void: if research == null: return + if not _is_research_active(research.id): + return + var amount_big: BigNumber = _amount var amount_float: float = amount_big.mantissa * pow(10.0, float(amount_big.exponent)) if amount_float <= 0.0: @@ -46,3 +49,24 @@ func _get_research_for_generator(generator_id: StringName) -> ResearchData: if game_state == null or game_state.research_catalogue == null: return null return game_state.research_catalogue.get_research_by_generator_id(generator_id) + +func _is_research_active(research_id: StringName) -> bool: + if game_state == null: + return false + + var research_panel: Node = 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 activate_research(research_id: StringName) -> void: + if game_state == null: + return + + var research_panel: Node = game_state.find_child("ResearchPanel", true, false) + if research_panel != null and research_panel.has_method("activate_research"): + research_panel.activate_research(research_id) diff --git a/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres b/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres index a0f193f..2e779bf 100644 --- a/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres +++ b/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres @@ -1,5 +1,6 @@ [gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://d08h51y0pnsnf"] +[ext_resource type="Resource" uid="uid://50yq2hl3wfwq" path="res://docs/gyms/tiny_sword/research/farm_research.tres" id="2_hprai"] [ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="3_bbypn"] [ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="4_0kvfm"] [ext_resource type="Resource" uid="uid://cts0407h130d6" path="res://docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres" id="5_qiy1b"] @@ -16,4 +17,5 @@ unlock_goal = ExtResource("5_qiy1b") initial_cost = 1.0 coefficient = 1.0 initial_productivity = 20.0 +research_data = ExtResource("2_hprai") metadata/_custom_type_script = "uid://b00tqsuhxdy0d" diff --git a/docs/gyms/tiny_sword/buildings/forestry/forestry.gd b/docs/gyms/tiny_sword/buildings/forestry/forestry.gd new file mode 100644 index 0000000..1e243a3 --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry.gd @@ -0,0 +1,13 @@ +extends Node2D + +@onready var _generator_container: GeneratorPanel = $GeneratorContainer + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + _generator_container.visible = false + +func _on_area_2d_mouse_entered() -> void: + _generator_container.visible = true + +func _on_area_2d_mouse_exited() -> void: + _generator_container.visible = false diff --git a/docs/gyms/tiny_sword/buildings/forestry/forestry.gd.uid b/docs/gyms/tiny_sword/buildings/forestry/forestry.gd.uid new file mode 100644 index 0000000..9b18cfb --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry.gd.uid @@ -0,0 +1 @@ +uid://cckf4nacd4qcr diff --git a/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn b/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn index 2f60802..d96e43a 100644 --- a/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn @@ -1,6 +1,7 @@ [gd_scene format=3 uid="uid://cf01wvy3f17i"] [ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://core/generator/currency_generator.tscn" id="1_7j1xc"] +[ext_resource type="Script" uid="uid://cckf4nacd4qcr" path="res://docs/gyms/tiny_sword/buildings/forestry/forestry.gd" id="1_l4bxt"] [ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="2_23x2t"] [ext_resource type="Resource" uid="uid://v6hjoa2vky5k" path="res://docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres" id="3_pby1g"] [ext_resource type="Texture2D" uid="uid://doxauaa3vn3lm" path="res://docs/gyms/tiny_sword/buildings/forestry/House3.png" id="4_5yp33"] @@ -10,6 +11,7 @@ size = Vector2(123, 157) [node name="Forestry" type="Node2D" unique_id=726284577] +script = ExtResource("1_l4bxt") [node name="CurrencyGenerator" parent="." unique_id=967969064 node_paths=PackedStringArray("info_generator_container") instance=ExtResource("1_7j1xc")] currency = ExtResource("2_23x2t") @@ -27,8 +29,12 @@ position = Vector2(0.5, -2.5) shape = SubResource("RectangleShape2D_v4aq3") [node name="GeneratorContainer" parent="." unique_id=1451609580 node_paths=PackedStringArray("_generator") instance=ExtResource("5_rh3m6")] +visible = false offset_left = 128.0 offset_top = -100.0 offset_right = 392.0 offset_bottom = 110.0 _generator = NodePath("../CurrencyGenerator") + +[connection signal="mouse_entered" from="Area2D" to="." method="_on_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Area2D" to="." method="_on_area_2d_mouse_exited"] diff --git a/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres b/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres index b89f60d..e954ea0 100644 --- a/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres @@ -1,6 +1,7 @@ [gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://v6hjoa2vky5k"] [ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="2_fs80u"] +[ext_resource type="Resource" uid="uid://cvow4sj6a5h23" path="res://docs/gyms/tiny_sword/research/forestry_research.tres" id="2_n4bxc"] [ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres" id="4_mgk3v"] [ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="4_rij12"] @@ -16,4 +17,5 @@ unlock_goal = ExtResource("4_mgk3v") initial_cost = 1.0 coefficient = 1.0 initial_productivity = 20.0 +research_data = ExtResource("2_n4bxc") metadata/_custom_type_script = "uid://b00tqsuhxdy0d" diff --git a/docs/gyms/tiny_sword/buildings/monastery/Monastery.png b/docs/gyms/tiny_sword/buildings/monastery/Monastery.png new file mode 100644 index 0000000..1b97b48 Binary files /dev/null and b/docs/gyms/tiny_sword/buildings/monastery/Monastery.png differ diff --git a/docs/gyms/tiny_sword/buildings/monastery/Monastery.png.import b/docs/gyms/tiny_sword/buildings/monastery/Monastery.png.import new file mode 100644 index 0000000..0ed877d --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/monastery/Monastery.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlmjd2berkb6v" +path="res://.godot/imported/Monastery.png-5f49b9547f0b0c767155d570a4d6cc55.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/gyms/tiny_sword/buildings/monastery/Monastery.png" +dest_files=["res://.godot/imported/Monastery.png-5f49b9547f0b0c767155d570a4d6cc55.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/gyms/tiny_sword/buildings/monastery/monastery.gd b/docs/gyms/tiny_sword/buildings/monastery/monastery.gd new file mode 100644 index 0000000..ad93604 --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/monastery/monastery.gd @@ -0,0 +1,14 @@ +extends Node2D + +@onready var _research_panel: ResearchPanel = $ResearchPanel + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + _research_panel.visible = false + +func _on_area_2d_mouse_entered() -> void: + _research_panel.visible = true + + +func _on_area_2d_mouse_exited() -> void: + _research_panel.visible = false diff --git a/docs/gyms/tiny_sword/buildings/monastery/monastery.gd.uid b/docs/gyms/tiny_sword/buildings/monastery/monastery.gd.uid new file mode 100644 index 0000000..d951735 --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/monastery/monastery.gd.uid @@ -0,0 +1 @@ +uid://cj0sxoceqqyrm diff --git a/docs/gyms/tiny_sword/buildings/monastery/monastery.tscn b/docs/gyms/tiny_sword/buildings/monastery/monastery.tscn new file mode 100644 index 0000000..c5ceae6 --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/monastery/monastery.tscn @@ -0,0 +1,30 @@ +[gd_scene format=3 uid="uid://bf8lqbexvnx6e"] + +[ext_resource type="Script" uid="uid://cj0sxoceqqyrm" path="res://docs/gyms/tiny_sword/buildings/monastery/monastery.gd" id="1_e17qd"] +[ext_resource type="Texture2D" uid="uid://dlmjd2berkb6v" path="res://docs/gyms/tiny_sword/buildings/monastery/Monastery.png" id="1_yyw6r"] +[ext_resource type="PackedScene" uid="uid://dd8roif0mqirl" path="res://core/research/research_panel.tscn" id="3_c6gi6"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_yyw6r"] +size = Vector2(160, 254) + +[node name="Monastery" type="Node2D" unique_id=1545930496] +script = ExtResource("1_e17qd") + +[node name="Sprite2D" type="Sprite2D" parent="." unique_id=2097167417] +texture = ExtResource("1_yyw6r") + +[node name="Area2D" type="Area2D" parent="." unique_id=926221537] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D" unique_id=1198096629] +position = Vector2(1, 16) +shape = SubResource("RectangleShape2D_yyw6r") + +[node name="ResearchPanel" parent="." unique_id=1105274967 instance=ExtResource("3_c6gi6")] +visible = false +offset_left = 78.0 +offset_top = -98.0 +offset_right = 478.0 +offset_bottom = 128.0 + +[connection signal="mouse_entered" from="Area2D" to="." method="_on_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Area2D" to="." method="_on_area_2d_mouse_exited"] diff --git a/docs/gyms/tiny_sword/buildings/monastery/monastery_generator.tres b/docs/gyms/tiny_sword/buildings/monastery/monastery_generator.tres new file mode 100644 index 0000000..a132f84 --- /dev/null +++ b/docs/gyms/tiny_sword/buildings/monastery/monastery_generator.tres @@ -0,0 +1,7 @@ +[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://56bnik1oe3ao"] + +[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="1_mnd3i"] + +[resource] +script = ExtResource("1_mnd3i") +metadata/_custom_type_script = "uid://b00tqsuhxdy0d" diff --git a/docs/gyms/tiny_sword/research/farm_research.tres b/docs/gyms/tiny_sword/research/farm_research.tres new file mode 100644 index 0000000..8ed9eea --- /dev/null +++ b/docs/gyms/tiny_sword/research/farm_research.tres @@ -0,0 +1,13 @@ +[gd_resource type="Resource" script_class="ResearchData" format=3 uid="uid://50yq2hl3wfwq"] + +[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="1_supp3"] +[ext_resource type="Script" uid="uid://m7baywrfnpn0" path="res://core/research/research_data.gd" id="2_rgx73"] + +[resource] +script = ExtResource("2_rgx73") +id = &"farm_research" +generator_id = &"farm" +name = "Farm Research" +description = "Research that improves farm production" +icon = ExtResource("1_supp3") +metadata/_custom_type_script = "uid://m7baywrfnpn0" diff --git a/docs/gyms/tiny_sword/research/forestry_research.tres b/docs/gyms/tiny_sword/research/forestry_research.tres new file mode 100644 index 0000000..d7c2741 --- /dev/null +++ b/docs/gyms/tiny_sword/research/forestry_research.tres @@ -0,0 +1,13 @@ +[gd_resource type="Resource" script_class="ResearchData" format=3 uid="uid://cvow4sj6a5h23"] + +[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="1_qvnpw"] +[ext_resource type="Script" uid="uid://m7baywrfnpn0" path="res://core/research/research_data.gd" id="2_6lndp"] + +[resource] +script = ExtResource("2_6lndp") +id = &"forestry_research" +generator_id = &"forestry" +name = "Forestry Research" +description = "Research that improves forestry production" +icon = ExtResource("1_qvnpw") +metadata/_custom_type_script = "uid://m7baywrfnpn0" diff --git a/docs/gyms/tiny_sword/research/gold_mine_research.tres b/docs/gyms/tiny_sword/research/gold_mine_research.tres index 603fef9..31640e6 100644 --- a/docs/gyms/tiny_sword/research/gold_mine_research.tres +++ b/docs/gyms/tiny_sword/research/gold_mine_research.tres @@ -5,14 +5,9 @@ [resource] script = ExtResource("1_yn72k") -id = &"gold_research" +id = &"gold_mine_research" generator_id = &"goldmine" name = "Gold Mine Research" description = "Research that improves gold mine production" icon = ExtResource("1_2tci1") -xp_per_currency_produced = 0.01 -base_xp_required = 100.0 -xp_growth_multiplier = 1.5 -base_multiplier = 1.0 -multiplier_per_level = 0.1 metadata/_custom_type_script = "uid://m7baywrfnpn0" diff --git a/docs/gyms/tiny_sword/research/ts_research_catalogue.tres b/docs/gyms/tiny_sword/research/ts_research_catalogue.tres index 65df7b2..432e718 100644 --- a/docs/gyms/tiny_sword/research/ts_research_catalogue.tres +++ b/docs/gyms/tiny_sword/research/ts_research_catalogue.tres @@ -3,8 +3,10 @@ [ext_resource type="Script" uid="uid://m7baywrfnpn0" path="res://core/research/research_data.gd" id="1_wscpr"] [ext_resource type="Resource" uid="uid://dif6w6kesmw6u" path="res://docs/gyms/tiny_sword/research/gold_mine_research.tres" id="2_migxn"] [ext_resource type="Script" uid="uid://d2v6t6w2todfy" path="res://core/research/research_catalogue.gd" id="2_yh8dx"] +[ext_resource type="Resource" uid="uid://50yq2hl3wfwq" path="res://docs/gyms/tiny_sword/research/farm_research.tres" id="3_pdfmu"] +[ext_resource type="Resource" uid="uid://cvow4sj6a5h23" path="res://docs/gyms/tiny_sword/research/forestry_research.tres" id="4_jnaqq"] [resource] script = ExtResource("2_yh8dx") -research_entries = Array[ExtResource("1_wscpr")]([ExtResource("2_migxn")]) +research_entries = Array[ExtResource("1_wscpr")]([ExtResource("2_migxn"), ExtResource("3_pdfmu"), ExtResource("4_jnaqq")]) metadata/_custom_type_script = "uid://d2v6t6w2todfy" diff --git a/docs/gyms/tiny_sword/tiny_sword.tscn b/docs/gyms/tiny_sword/tiny_sword.tscn index e4807e5..c534522 100644 --- a/docs/gyms/tiny_sword/tiny_sword.tscn +++ b/docs/gyms/tiny_sword/tiny_sword.tscn @@ -10,6 +10,7 @@ [ext_resource type="Script" uid="uid://srkiu4qe8s2m" path="res://core/prestige/prestige_manager.gd" id="5_x77df"] [ext_resource type="Resource" uid="uid://dwmfvmusfskk6" path="res://docs/gyms/tiny_sword/prestige/primary_prestige.tres" id="6_xnhlc"] [ext_resource type="PackedScene" uid="uid://btkxru2gdjsgc" path="res://currency_tile.tscn" id="7_0cs5o"] +[ext_resource type="Script" uid="uid://bv3a4utlnn2fg" path="res://core/research/research_xp_tracker.gd" id="8_no27p"] [ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="9_1363k"] [ext_resource type="PackedScene" uid="uid://djedqovgngrx5" path="res://docs/gyms/tiny_sword/buildings/farm/farm.tscn" id="10_1lv5i"] [ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="10_i1cck"] @@ -17,9 +18,9 @@ [ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="11_hskcg"] [ext_resource type="PackedScene" uid="uid://cf01wvy3f17i" path="res://docs/gyms/tiny_sword/buildings/forestry/forestry.tscn" id="11_pyqyw"] [ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="12_l6a68"] +[ext_resource type="PackedScene" uid="uid://bf8lqbexvnx6e" path="res://docs/gyms/tiny_sword/buildings/monastery/monastery.tscn" id="13_no27p"] [ext_resource type="PackedScene" uid="uid://rejxvjwybkll" path="res://sandbox/tiny_swords/Terrain/Resources/Wood/Trees/tree_1.tscn" id="14_0cs5o"] [ext_resource type="Resource" uid="uid://bfrb0ayrljac2" path="res://docs/gyms/tiny_sword/currencies/ascension.tres" id="15_ascension"] -[ext_resource type="PackedScene" uid="uid://dd8roif0mqirl" path="res://core/research/research_panel.tscn" id="21_no27p"] [node name="TinySwords" type="Node" unique_id=498237642] @@ -36,6 +37,10 @@ script = ExtResource("5_x77df") config = ExtResource("6_xnhlc") game_state = NodePath("..") +[node name="ResearchXpTracker" type="Node" parent="LevelGameState" unique_id=229654635 node_paths=PackedStringArray("game_state")] +script = ExtResource("8_no27p") +game_state = NodePath("..") + [node name="World" type="Node2D" parent="LevelGameState" unique_id=2118297724] [node name="ForestProps" type="Node2D" parent="LevelGameState/World" unique_id=649424542] @@ -82,6 +87,9 @@ position = Vector2(380, 684) [node name="Forestry" parent="LevelGameState/World" unique_id=726284577 instance=ExtResource("11_pyqyw")] position = Vector2(825, 915) +[node name="Monastery" parent="LevelGameState/World" unique_id=1545930496 instance=ExtResource("13_no27p")] +position = Vector2(105, 920) + [node name="UI" type="Control" parent="LevelGameState" unique_id=1299828389] layout_mode = 3 anchors_preset = 0 @@ -133,15 +141,3 @@ offset_left = 1275.0 offset_top = 4.0 offset_right = 1913.0 offset_bottom = 263.0 - -[node name="ResearchPanel" parent="LevelGameState/UI" unique_id=2145420920 instance=ExtResource("21_no27p")] -custom_minimum_size = Vector2(350, 100) -layout_mode = 1 -anchors_preset = -1 -anchor_left = 7.4500003 -anchor_right = 16.2 -anchor_bottom = 3.1750002 -offset_right = 0.0 -offset_bottom = -7.6293945e-06 -grow_horizontal = 2 -grow_vertical = 2 diff --git a/tests/test_gold_mine_click.gd b/tests/test_gold_mine_click.gd index 1d50e93..8c64b7c 100644 --- a/tests/test_gold_mine_click.gd +++ b/tests/test_gold_mine_click.gd @@ -6,10 +6,7 @@ var _game_root: Node = null func _ready(): await run() - if failed == 0: - get_tree().quit(0) - else: - get_tree().quit(1) + # Don't quit here - let test_runner handle it func run(): print("\n=== TEST: Gold Mine Click ===\n") diff --git a/tests/test_prestige.gd b/tests/test_prestige.gd index 00d80de..6befcab 100644 --- a/tests/test_prestige.gd +++ b/tests/test_prestige.gd @@ -7,9 +7,9 @@ var _game_root: Node = null func _ready(): await run() if failed == 0: - get_tree().quit(0) + # get_tree().quit(0) // Let test_runner handle it else: - get_tree().quit(1) + # get_tree().quit(1) // Let test_runner handle it func run(): print("\n=== TEST: Prestige Mechanics ===\n") diff --git a/tests/test_research_activation.gd b/tests/test_research_activation.gd new file mode 100644 index 0000000..74c0c6b --- /dev/null +++ b/tests/test_research_activation.gd @@ -0,0 +1,159 @@ +extends SceneTree + +var passed: int = 0 +var failed: int = 0 +var _game_root: Node = null +var _local_passed: int = 0 +var _local_failed: int = 0 + +func _init(): + await run() + +func run(): + print("\n=== TEST: Research Activation ===\n") + TestUtils.set_test_name("research_activation") + + var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn") + _game_root = scene.instantiate() + root.add_child(_game_root) + + await _wait() + + var game_state: LevelGameState = _game_root.find_child("LevelGameState") + var research_panel: ResearchPanel = _game_root.find_child("ResearchPanel") as ResearchPanel + + if game_state == null or research_panel == null: + print("[ERROR] Missing LevelGameState or ResearchPanel node") + _print_summary() + quit(1) + return + + var gold_mine_node = game_state.find_child("GoldMine") + var gold_mine: CurrencyGenerator = gold_mine_node.find_child("CurrencyGenerator") as CurrencyGenerator if gold_mine_node else null + + if gold_mine == null: + print("[ERROR] GoldMine node not found") + _print_summary() + quit(1) + return + + # Test 1: Verify initial state - no research should be active + print("[TARGET] initial_active_research_id: %s" % research_panel.get_active_research_id()) + TestUtils.assert_true( + research_panel.get_active_research_id() == &"", + "No research active initially" + ) + + # Test 2: Verify gold mine research exists and is linked to goldmine generator + var gold_research = game_state.research_catalogue.get_research_by_id(&"gold_research") + TestUtils.assert_not_null( + gold_research, + "Gold research exists in catalogue" + ) + + if gold_research != null: + TestUtils.assert_true( + gold_research.generator_id == &"goldmine", + "Gold research is linked to goldmine generator" + ) + + # Test 3: Activate gold research + research_panel.activate_research(&"gold_research") + await _wait() + + print("[TARGET] active_after_gold_activation: %s" % research_panel.get_active_research_id()) + TestUtils.assert_true( + research_panel.get_active_research_id() == &"gold_research", + "Gold research is active after activation" + ) + + TestUtils.assert_true( + research_panel.is_research_active(&"gold_research"), + "is_research_active returns true for gold_research" + ) + + # Test 4: Verify research XP is granted when research is active + var initial_gold_research_xp = game_state.get_research_xp(&"gold_research") + print("[TARGET] initial_gold_research_xp: %s" % initial_gold_research_xp.to_string_suffix(2)) + + # Simulate gold mine production (gold mine grants XP to gold_research) + gold_mine._on_pressed() + await _wait() + + var final_gold_research_xp_active = game_state.get_research_xp(&"gold_research") + print("[TARGET] final_gold_research_xp_active: %s" % final_gold_research_xp_active.to_string_suffix(2)) + + TestUtils.assert_true( + final_gold_research_xp_active.mantissa > initial_gold_research_xp.mantissa, + "Gold research XP increases when active (gold mine produces)" + ) + + # Test 5: Deactivate gold research by activating another research + var farm_research = game_state.research_catalogue.get_research_by_id(&"farm_research") + TestUtils.assert_not_null( + farm_research, + "Farm research exists in catalogue" + ) + + if farm_research != null: + # Activate farm research (should deactivate gold research) + research_panel.activate_research(&"farm_research") + await _wait() + + print("[TARGET] active_after_farm_activation: %s" % research_panel.get_active_research_id()) + TestUtils.assert_true( + research_panel.get_active_research_id() == &"farm_research", + "Farm research is active after activation" + ) + + TestUtils.assert_false( + research_panel.is_research_active(&"gold_research"), + "Gold research is deactivated when farm is activated" + ) + + # Test 6: Verify research XP is NOT granted when research is inactive + var initial_gold_research_xp_inactive = game_state.get_research_xp(&"gold_research") + print("[TARGET] initial_gold_research_xp_inactive: %s" % initial_gold_research_xp_inactive.to_string_suffix(2)) + + # Simulate gold mine production (gold mine should NOT grant XP to inactive gold_research) + gold_mine._on_pressed() + await _wait() + + var final_gold_research_xp_inactive = game_state.get_research_xp(&"gold_research") + print("[TARGET] final_gold_research_xp_inactive: %s" % final_gold_research_xp_inactive.to_string_suffix(2)) + + TestUtils.assert_equals( + final_gold_research_xp_inactive.mantissa, + initial_gold_research_xp_inactive.mantissa, + "Gold research XP does not increase when inactive (gold mine produces)" + ) + + # Test 7: Test mutual exclusivity with multiple activations + research_panel.activate_research(&"gold_research") + await _wait() + research_panel.activate_research(&"farm_research") + await _wait() + research_panel.activate_research(&"gold_research") + await _wait() + + print("[TARGET] final_active_research_id: %s" % research_panel.get_active_research_id()) + TestUtils.assert_true( + research_panel.get_active_research_id() == &"gold_research", + "Only one research can be active at a time" + ) + + _print_summary() + if failed == 0: + quit(0) + else: + quit(1) + +func _wait() -> void: + await create_timer(0.5).timeout + +func _print_summary(): + TestUtils.print_result() + _local_passed = TestUtils.get_passed() + _local_failed = TestUtils.get_failed() + passed = _local_passed + failed = _local_failed diff --git a/tests/test_research_activation.gd.uid b/tests/test_research_activation.gd.uid new file mode 100644 index 0000000..fd20761 --- /dev/null +++ b/tests/test_research_activation.gd.uid @@ -0,0 +1 @@ +uid://cf23bjqannrog diff --git a/tests/test_runner.gd b/tests/test_runner.gd index ede31e2..2ffd46a 100644 --- a/tests/test_runner.gd +++ b/tests/test_runner.gd @@ -1,18 +1,18 @@ -extends Node +extends SceneTree var test_scripts: Array[String] = [] var test_index: int = 0 var total_passed: int = 0 var total_failed: int = 0 -func _ready(): +func _init(): print("\n=== GODOT TEST RUNNER ===\n") _discover_tests() if test_scripts.is_empty(): print("[ERROR] No test scripts found in res://tests/") print("[RESULT] FAIL") - get_tree().quit(1) + quit(1) return print("Found %d test(s): %s\n" % [test_scripts.size(), str(test_scripts)]) @@ -25,10 +25,10 @@ func _ready(): if total_failed == 0: print("[OVERALL_RESULT] PASS") - get_tree().quit(0) + quit(0) else: print("[OVERALL_RESULT] FAIL") - get_tree().quit(1) + quit(1) func _discover_tests() -> void: var dir = DirAccess.open("res://tests") @@ -53,11 +53,26 @@ func _run_next_test() -> void: # Load and run test var test_instance = load(test_script).new() - add_child(test_instance) - await test_instance.run() + get_root().add_child(test_instance) - total_passed += test_instance.get_passed() - total_failed += test_instance.get_failed() + # Check if test has run() method (new pattern) or uses _ready() (old pattern) + if test_instance.has_method("run"): + await test_instance.run() + # Clean up test instance + test_instance.queue_free() + else: + # For old pattern, wait a bit for _ready() to complete + await create_timer(0.2).timeout + # Clean up test instance + test_instance.queue_free() + + # Get results if available + if test_instance.has_method("get_passed"): + total_passed += test_instance.get_passed() + total_failed += test_instance.get_failed() + + # Wait for cleanup + await create_timer(0.1).timeout print("") test_index += 1