Fix research xp generation

This commit is contained in:
2026-04-18 00:56:44 +02:00
parent 2bc81a73fd
commit dfb1161854
29 changed files with 485 additions and 47 deletions

View File

@@ -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
@@ -406,16 +434,22 @@ 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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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,14 +26,18 @@ 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():
_refresh_row(row)
@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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"

View File

@@ -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

View File

@@ -0,0 +1 @@
uid://cckf4nacd4qcr

View File

@@ -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"]

View File

@@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
uid://cj0sxoceqqyrm

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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")

View File

@@ -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")

View File

@@ -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

View File

@@ -0,0 +1 @@
uid://cf23bjqannrog

View File

@@ -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,12 +53,27 @@ 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)
# 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
await _run_next_test()