196 lines
9.6 KiB
GDScript
196 lines
9.6 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, _damage_type: int = 0) -> 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 == 0)
|
|
await _collect_credits(game.get_node("PlayerShip") as CharacterBody2D)
|
|
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)
|
|
await _collect_credits(player)
|
|
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)
|
|
player = game.get_node("PlayerShip") as CharacterBody2D
|
|
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)
|
|
await _collect_credits(player)
|
|
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 _collect_credits(player: CharacterBody2D) -> void:
|
|
await get_tree().process_frame
|
|
for pickup in get_tree().get_nodes_in_group("credit_pickups"):
|
|
player.global_position = pickup.global_position
|
|
pickup.call("_physics_process", 0.0)
|
|
|
|
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)
|