141 lines
6.3 KiB
GDScript
141 lines
6.3 KiB
GDScript
extends Node2D
|
|
|
|
const PLAYER := preload("res://scenes/game/player_ship.tscn")
|
|
const ENEMY := preload("res://scenes/game/enemy_ship.tscn")
|
|
const GENERATOR := preload("res://scripts/items/item_generator.gd")
|
|
const IDS := ["teleport", "cloak", "mine_layer", "flares", "solar_shield_charge"]
|
|
var ship: CharacterBody2D
|
|
var inventory: ShipInventory
|
|
|
|
func equip(id: String, slot: int) -> void:
|
|
var item := GENERATOR.generate_for_definition(load("res://resources/items/%s.tres" % id), 1)
|
|
assert(item.additional_modifiers.is_empty())
|
|
assert(item.definition.icon != null)
|
|
assert(inventory.add_item(item))
|
|
assert(inventory.move_item("cargo", inventory.cargo.find(item), "extension", slot))
|
|
|
|
func _ready() -> void:
|
|
GameState.start_new_run()
|
|
ship = PLAYER.instantiate() as CharacterBody2D
|
|
ship.name = "PlayerShip"
|
|
add_child(ship)
|
|
ship.set_physics_process(false)
|
|
ship.get_node("WeaponController").set_physics_process(false)
|
|
var systems := ship.get_node("ExtensionSystems")
|
|
systems.set_physics_process(false)
|
|
inventory = ship.get_node("ShipInventory") as ShipInventory
|
|
for index in IDS.size():
|
|
equip(IDS[index], index)
|
|
var enemy := ENEMY.instantiate() as CharacterBody2D
|
|
enemy.position = Vector2(300, 0)
|
|
add_child(enemy)
|
|
enemy.set("_player", ship)
|
|
enemy.set_physics_process(false)
|
|
await get_tree().physics_frame
|
|
await get_tree().process_frame
|
|
# Teleport: partial energy is fully consumed; landing stays inside the playable area.
|
|
var origin := ship.position
|
|
ship.set("energy", 37.0)
|
|
assert(ship.call("activate_ability", 0))
|
|
assert(ship.position.distance_to(origin) >= 400.0)
|
|
assert(absf(ship.position.x) <= 2440 and absf(ship.position.y) <= 1640)
|
|
assert(ship.get("energy") == 0.0)
|
|
assert(ship.call("get_ability_cooldown", 0) == 60.0)
|
|
assert(not ship.call("activate_ability", 0))
|
|
# Moving a module or equipping a duplicate cannot bypass its cooldown.
|
|
assert(inventory.move_item("extension", 0, "extension", 1))
|
|
assert(ship.call("get_ability_cooldown", 1) == 60.0)
|
|
equip("teleport", 4)
|
|
assert(ship.call("get_ability_cooldown", 4) == 60.0)
|
|
assert(not ship.call("activate_ability", 4))
|
|
equip("solar_shield_charge", 4)
|
|
assert(inventory.move_item("extension", 1, "extension", 0))
|
|
ship.position = origin
|
|
enemy.position = origin + Vector2(300, 0)
|
|
assert(enemy.call("select_combat_target") == ship)
|
|
# Cloak removes targeting, lasts ten seconds and restores the original artwork.
|
|
var original_material: Material = ship.get_node("Sprite2D").material
|
|
assert(ship.call("activate_ability", 1))
|
|
assert(ship.call("is_cloaked"))
|
|
assert(enemy.call("select_combat_target") == null)
|
|
assert(ship.get_node("Sprite2D").material != original_material)
|
|
assert(not ship.call("activate_ability", 1))
|
|
# Flares attract enemies even while the player is invisible, and expire in ten seconds.
|
|
assert(ship.call("activate_ability", 3))
|
|
assert(ship.call("get_ability_cooldown", 3) == 30.0)
|
|
var flares := get_tree().get_nodes_in_group("player_flares")
|
|
assert(flares.size() == 3)
|
|
assert(flares.has(enemy.call("select_combat_target")))
|
|
assert(not ship.call("activate_ability", 3))
|
|
for flare in flares:
|
|
flare.set_physics_process(false)
|
|
flare.call("_physics_process", 10.0)
|
|
assert(flare.is_queued_for_deletion())
|
|
assert(enemy.call("select_combat_target") == null)
|
|
systems.call("_physics_process", 9.9)
|
|
assert(ship.call("is_cloaked"))
|
|
systems.call("_physics_process", 0.11)
|
|
assert(not ship.call("is_cloaked"))
|
|
assert(ship.get_node("Sprite2D").material == original_material)
|
|
assert(enemy.call("select_combat_target") == ship)
|
|
assert(not ship.call("activate_ability", 1))
|
|
# Mine: 5% maximum hull damage despite armor; slow lasts five seconds; one hit only.
|
|
assert(ship.call("activate_ability", 2))
|
|
var mine: Node2D = get_tree().get_nodes_in_group("player_mines")[0]
|
|
mine.set_physics_process(false)
|
|
enemy.position = mine.position
|
|
enemy.set("shields", 0.0)
|
|
enemy.set("absorption", Vector3(75, 0, 0))
|
|
var maximum := float(enemy.get("max_hull"))
|
|
mine.call("_physics_process", 0.8)
|
|
assert(mine.is_queued_for_deletion())
|
|
assert(is_equal_approx(float(enemy.get("hull")), maximum * 0.95))
|
|
mine.call("_physics_process", 0.1)
|
|
assert(is_equal_approx(float(enemy.get("hull")), maximum * 0.95))
|
|
assert(enemy.call("get_slow_multiplier") == 0.5)
|
|
enemy.set("_player", null)
|
|
enemy.call("_physics_process", 5.1)
|
|
assert(enemy.call("get_slow_multiplier") == 1.0)
|
|
# Solar shields: blocked away from stars and at full shields; fills in five seconds.
|
|
ship.set("shields", 0.0)
|
|
assert(not ship.call("activate_ability", 4))
|
|
var star := Node2D.new()
|
|
star.position = ship.position
|
|
add_child(star)
|
|
ship.set("charging_star", star)
|
|
assert(ship.call("activate_ability", 4))
|
|
assert(ship.call("get_ability_cooldown", 4) == 30.0)
|
|
systems.call("_physics_process", 2.5)
|
|
assert(is_equal_approx(float(ship.get("shields")), float(ship.get("max_shields")) * 0.5))
|
|
systems.call("_physics_process", 2.5)
|
|
assert(is_equal_approx(float(ship.get("shields")), float(ship.get("max_shields"))))
|
|
systems.get("cooldowns").clear()
|
|
assert(not ship.call("activate_ability", 4))
|
|
ship.set("shields", 0.0)
|
|
assert(ship.call("activate_ability", 4))
|
|
ship.position += Vector2(1000, 0)
|
|
systems.call("_physics_process", 1.0)
|
|
assert(ship.get("shields") == 0.0 and systems.get("solar_remaining") == 0.0)
|
|
# Docking, defeat and pause prevent activation; expired cooldowns allow reuse.
|
|
systems.call("_physics_process", 61.0)
|
|
ship.set("is_docked", true)
|
|
assert(not ship.call("activate_ability", 3))
|
|
ship.set("is_docked", false)
|
|
get_tree().paused = true
|
|
assert(not ship.call("activate_ability", 3))
|
|
get_tree().paused = false
|
|
ship.set("_is_destroyed", true)
|
|
assert(not ship.call("activate_ability", 3))
|
|
ship.set("_is_destroyed", false)
|
|
assert(ship.call("activate_ability", 3))
|
|
inventory.preserve_for_restart()
|
|
assert(not GameState.restart_inventory.has("extension_cooldowns"))
|
|
var restored := PLAYER.instantiate() as CharacterBody2D
|
|
add_child(restored)
|
|
restored.set_physics_process(false)
|
|
restored.get_node("WeaponController").set_physics_process(false)
|
|
assert(restored.call("get_ability_cooldown", 3) == 0.0)
|
|
assert(restored.call("activate_ability", 3))
|
|
print("TACTICAL_EXTENSIONS_TEST: PASS (five modules, icons, loot, teleport safety/energy, cloak duration/AI/restoration, flares targeting/lifetime, mine damage/slow/single hit, solar recharge/range, shared cooldowns, pause/dock/defeat, cooldown reset on reload)")
|
|
get_tree().quit()
|