Randoloot/tests/captain/captain_system_test.gd
2026-10-11 17:16:45 +02:00

185 lines
9.1 KiB
GDScript

extends Node
const PROGRESS := preload("res://scripts/captain/captain_progress.gd")
const COMBAT := preload("res://scripts/game/combat_rolls.gd")
const GAME_SCENE := preload("res://scenes/game/game.tscn")
const GENERATOR := preload("res://scripts/items/item_generator.gd")
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
const BALANCE := preload("res://scripts/captain/captain_balance.gd")
class DamageReceiver:
extends Node2D
var hits: Array[Dictionary] = []
func take_weapon_hit(amount: float, critical: bool) -> void:
hits.append({"damage": amount, "critical": critical})
func _ready() -> void:
_test_skill_balance_and_reset()
var profile := PROGRESS.new()
assert(not profile.train_skill("gunner"))
profile.add_rewards(99, 60)
assert(profile.level == 1 and profile.experience == 99)
profile.add_rewards(1, 15)
assert(profile.level == 2 and profile.experience == 0 and profile.learning_points == 1 and profile.credits == 75)
assert(profile.train_skill("gunner") and profile.get_skill_rank("gunner") == 1)
assert(not profile.train_skill("piloting") and not profile.train_skill("unknown"))
profile.add_rewards(450, 0)
assert(profile.level == 4 and profile.experience == 100 and profile.learning_points == 2)
profile.skills["piloting"] = 100
assert(not profile.train_skill("piloting"))
assert(profile.reward_enemy(10, 2, 1.25, 1.5) == Vector2i(272, 261))
var hit := COMBAT.weapon_hit(10, 1.1, 0, 2, 0)
assert(not hit["critical"] and is_equal_approx(hit["damage"], 11))
hit = COMBAT.weapon_hit(10, 1.1, 1, 2.4, 1)
assert(hit["critical"] and is_equal_approx(hit["damage"], 26.4))
assert(not COMBAT.weapon_hit(10, 1, 0.01, 2, 0.01)["critical"])
assert(COMBAT.incoming_hit(100, 1, 0, 1)["evaded"])
assert(is_equal_approx(COMBAT.incoming_hit(100, 0.01, 0.5, 0.01)["damage"], 50))
assert(COMBAT.incoming_hit(100, 0, 1, 0)["damage"] == 0)
assert(COMBAT.incoming_hit(-100, 0, 0, 0)["damage"] == 0)
GameState.start_new_run()
var game := GAME_SCENE.instantiate() as Node2D
get_tree().root.call_deferred("add_child", game)
await get_tree().process_frame
await get_tree().process_frame
get_tree().current_scene = game
_freeze(game)
profile = GameState.captain
var enemies := get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
enemy.call("take_weapon_hit", 1000000.0, true)
enemy.call("take_damage", 1000000.0)
assert(profile.level == 2 and profile.experience == 130 and profile.learning_points == 1)
assert(profile.credits == 180)
var panel := game.get_node("HUD/HUDRoot/CaptainPanel")
panel.get_node("Contents/Skills/gunner/Train_gunner").emit_signal("pressed")
assert(profile.get_skill_rank("gunner") == 1 and profile.learning_points == 0)
panel.get_node("Contents/Skills/piloting/Train_piloting").emit_signal("pressed")
assert(profile.get_skill_rank("piloting") == 0)
assert(panel.get_node("Contents/Header/Level").get("text") == "POZ. 2")
var player := game.get_node("PlayerShip") as CharacterBody2D
profile.skills["piloting"] = 100
assert(is_equal_approx(float(player.call("get_evasion_chance")), 0.25))
assert(COMBAT.incoming_hit(100, player.call("get_evasion_chance"), 0, 0.0)["evaded"])
assert(COMBAT.incoming_hit(100, player.call("get_evasion_chance"), 0, 0.25)["damage"] == 100)
profile.skills["piloting"] = 0
profile.skills["mechanic"] = 15
player.set("shields", 100.0) # Fixed fixture, independent of selected chassis capacity.
player.call("take_damage", 100.0)
assert(is_equal_approx(float(player.get("shields")), 30) and float(player.get("hull")) == 100)
player.set("shields", 0.0)
player.call("take_damage", 40.0)
assert(is_equal_approx(float(player.get("hull")), 72))
profile.skills["mechanic"] = 100
assert(is_equal_approx(float(player.call("get_damage_reduction")), 1.0 / 3.0))
player.call("take_damage", 30.0)
assert(is_equal_approx(float(player.get("hull")), 52))
profile.skills["mechanic"] = 0
profile.skills["sharp_eye"] = 1
var inventory := player.get_node("ShipInventory") as ShipInventory
var weapon := GENERATOR.generate_for_definition(preload("res://resources/items/plasma_cannon.tres"), 1)
weapon.additional_modifiers.clear()
weapon.damage_min = 10
weapon.damage_max = 10
for data in [["critical_chance_percent", 25.0], ["critical_damage_percent", 20.0], ["damage_percent", 10.0], ["experience_percent", 25.0], ["credits_percent", 50.0]]:
var modifier := ItemStatModifier.new()
modifier.stat_key = data[0]
modifier.value = data[1]
weapon.additional_modifiers.append(modifier)
var free_slot := inventory.cargo.find(null)
assert(inventory.add_item(weapon) and inventory.move_item("cargo", free_slot, "weapon", 0))
assert(is_equal_approx(float(player.call("get_critical_chance")), 0.26))
assert(is_equal_approx(float(player.call("get_critical_multiplier")), 2.4))
assert(is_equal_approx(float(player.call("get_weapon_damage_multiplier")), 1.122))
profile.skills["sharp_eye"] = 20
assert(is_equal_approx(float(player.call("get_critical_chance")), 0.45))
player.set("critical_chance_bonus", 0.8) # Force this metadata check independently of RNG.
hit = player.call("roll_weapon_hit", weapon)
assert(hit["critical"] and is_equal_approx(hit["damage"], 26.928))
player.set("critical_chance_bonus", 0.25)
var receiver := DamageReceiver.new()
game.add_child(receiver)
var projectile := PROJECTILE.instantiate()
projectile.call("configure", Vector2.ZERO, Vector2.RIGHT, ItemDefinition.WeaponKind.MISSILE, 400.0, 26.928, 1000.0, null, true)
game.add_child(projectile)
projectile.set_physics_process(false)
projectile.call("_apply_damage_once", receiver)
projectile.call("_apply_damage_once", receiver)
assert(receiver.hits.size() == 1 and receiver.hits[0]["critical"])
assert(is_equal_approx(receiver.hits[0]["damage"], 26.928))
player.call("take_damage", 1000000.0)
await get_tree().create_timer(1.3).timeout
game = get_tree().current_scene as Node2D
_freeze(game)
assert(GameState.captain == profile and profile.level == 2 and profile.credits == 180)
player = game.get_node("PlayerShip") as CharacterBody2D
assert(player.get_node("ShipInventory").call("get_item", "weapon", 0) == weapon)
for enemy in get_tree().get_nodes_in_group("enemies"):
enemy.call("take_damage", 1000000.0)
assert(profile.level == 4 and profile.experience == 68 and profile.credits == 452)
var planet: Node2D = game.get("protected_planet")
player.position = planet.position
game.call("_physics_process", 0.0)
assert(bool(game.call("open_wormhole")))
var portal: Node2D = game.get("wormhole")
player.position = portal.position
assert(bool(game.call("advance_to_next_level")))
await get_tree().process_frame
await get_tree().process_frame
await get_tree().process_frame
game = get_tree().current_scene as Node2D
_freeze(game)
assert(GameState.captain == profile and GameState.system_level == 2)
assert(profile.get_skill_rank("gunner") == 1 and profile.get_skill_rank("sharp_eye") == 20)
var enemy := get_tree().get_nodes_in_group("enemies")[0] as CharacterBody2D
enemy.call("take_damage", 1000000.0)
assert(profile.level == 5 and profile.experience == 15 and profile.credits == 641)
print("CAPTAIN_SYSTEM_TEST: PASS (XP, multiple levels, learning UI, rewards once, dodge, reduction, critical addons, projectile metadata, death and next system)")
func _test_skill_balance_and_reset() -> void:
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 1), 0.5 / 11.0))
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 5), 1.0 / 6.0))
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 10), 0.25))
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 20), 1.0 / 3.0))
assert(is_equal_approx(BALANCE.skill_bonus("piloting", 20), 0.25))
assert(is_equal_approx(BALANCE.skill_bonus("gunner", 20), 0.4))
assert(is_equal_approx(BALANCE.skill_bonus("sharp_eye", 20), 0.2))
for skill in ["mechanic", "piloting"]:
var previous_gain := 1.0
for rank in range(1, 21):
var gain := BALANCE.skill_bonus(skill, rank) - BALANCE.skill_bonus(skill, rank - 1)
assert(gain > 0.0 and gain < previous_gain)
previous_gain = gain
var captain := PROGRESS.new()
captain.learning_points = 100
for rank in 20:
assert(captain.train_skill("mechanic"))
assert(not captain.train_skill("mechanic") and captain.learning_points == 80)
captain.level = 3
captain.credits = 299
assert(not captain.reset_skills())
assert(captain.skills["mechanic"] == 20 and captain.credits == 299)
captain.credits = 300
assert(captain.reset_skills())
assert(captain.learning_points == 100 and captain.credits == 0 and captain.skills["mechanic"] == 0)
captain.credits = 300
assert(not captain.reset_skills() and captain.credits == 300)
for skill in BALANCE.SKILL_NAMES:
captain.skills[skill] = 100
captain.learning_points = 7
captain.normalize_skill_ranks()
assert(captain.learning_points == 327 and captain.get_spent_learning_points() == 80)
captain.normalize_skill_ranks()
assert(captain.learning_points == 327)
assert(captain.reset_skills())
assert(captain.learning_points == 407)
func _freeze(game: Node2D) -> void:
game.set_physics_process(false)
game.get_node("PlayerShip").set_physics_process(false)
game.get_node("PlayerShip/WeaponController").set_physics_process(false)
for enemy in get_tree().get_nodes_in_group("enemies"):
if game.is_ancestor_of(enemy):
enemy.set_physics_process(false)