Randoloot/tests/ships/laboratory_test.gd
2026-10-26 19:33:45 +01:00

173 lines
8.4 KiB
GDScript

extends Node2D
const LAB := preload("res://scripts/planets/laboratory.gd")
const GEN := preload("res://scripts/items/item_generator.gd")
var player: CharacterBody2D
class StraightLaser extends "res://scripts/game/weapon_controller.gd":
func _aim_direction_from(_origin: Vector2) -> Vector2: return Vector2.RIGHT
func target_at(point: Vector2) -> CharacterBody2D:
var enemy := preload("res://scenes/game/enemy_ship.tscn").instantiate()
add_child(enemy)
enemy.set_physics_process(false)
enemy.position = point
enemy.max_hull = 10000
enemy.hull = 10000
enemy.shields = 0
enemy.absorption = Vector3.ZERO
enemy._player = player
return enemy
func snapshot(path: String) -> void:
for i in 8: await get_tree().process_frame
await RenderingServer.frame_post_draw
get_viewport().get_texture().get_image().save_png(path)
func _ready() -> void:
seed(49141)
GameState.start_new_run()
var captain := GameState.captain
var progress: Resource = captain.expedition
LAB.ensure_offers(progress)
assert(progress.laboratory_offers.size()==3)
var offers: Array = progress.laboratory_offers.duplicate()
LAB.ensure_offers(progress)
assert(progress.laboratory_offers==offers,"Opening laboratory does not reroll offers")
for definition in GEN.available_definitions():
assert(definition.category != ItemDefinition.Category.ACTIVE_EXTENSION)
assert(definition.category != ItemDefinition.Category.WEAPON or definition.item_id == "plasma_cannon")
for definition in GEN.DEFINITIONS:
if definition.category == ItemDefinition.Category.ACTIVE_EXTENSION or (definition.category == ItemDefinition.Category.WEAPON and definition.item_id != "plasma_cannon"):
assert(definition.item_id in LAB.IDS)
for id in LAB.IDS:
assert(LAB.definition(id) != null and LAB.COSTS.has(id))
assert(not LAB.start(captain,offers[0]),"Laboratory must be built")
progress.freighter_rooms.expansion_1 = "laboratory"
assert(not LAB.start(captain,offers[0]),"Cannot research without materials")
for cost in LAB.COSTS.values():
for key in cost: captain.minerals[key] = 1000
# Fix the first choice for the weapon integration checks below.
progress.laboratory_offers.assign(["incendiary_missile", "radiation_laser", "artillery_cannon"])
offers = progress.laboratory_offers.duplicate()
var game := preload("res://scenes/game/game.tscn").instantiate()
add_child(game)
game.set_physics_process(false)
player = game.player
player.set_physics_process(false)
player.get_node("WeaponController").set_physics_process(false)
for enemy in get_tree().get_nodes_in_group("enemies"): enemy.set_physics_process(false)
var manager := game.get_node("PlanetExpeditions")
manager.open_freighter()
manager.screen._select_section("expansion_1")
assert(manager.screen._content.find_child("Research_incendiary_missile",true,false)!=null)
await snapshot("res://artifacts/laboratory_projects.png")
var before: Dictionary = captain.minerals.duplicate()
assert(LAB.start(captain,"incendiary_missile"))
for key in LAB.COSTS.incendiary_missile: assert(captain.minerals[key]==before[key]-LAB.COSTS.incendiary_missile[key])
assert(not LAB.start(captain,"radiation_laser"),"Only one project at a time")
GameState.advance_play_time(30.0,game)
assert(progress.laboratory_remaining==30,"Research advances while docked without any mining stations")
assert(not progress.laboratory_unlocks.has("incendiary_missile"))
assert(ResourceSaver.save(progress,"user://laboratory_progress_test.tres")==OK)
var saved: Resource = ResourceLoader.load("user://laboratory_progress_test.tres","",ResourceLoader.CACHE_MODE_IGNORE)
assert(saved.laboratory_project=="incendiary_missile" and saved.laboratory_remaining==30 and saved.laboratory_offers==offers)
await snapshot("res://artifacts/laboratory_progress.png")
manager._leave()
get_tree().paused = true
GameState.advance_play_time(30,game)
assert(progress.laboratory_remaining==30,"Ordinary pause stops research")
get_tree().paused = false
GameState.advance_play_time(29.9,game)
assert(not progress.laboratory_unlocks.has("incendiary_missile"))
GameState.advance_play_time(0.1,game)
assert(progress.laboratory_unlocks.has("incendiary_missile") and progress.laboratory_offers.size()==3)
assert(GEN.available_definitions().has(LAB.definition("incendiary_missile")))
while not progress.laboratory_offers.is_empty():
var id: String = progress.laboratory_offers[0]
assert(not GEN.available_definitions().has(LAB.definition(id)))
assert(LAB.start(captain,id))
LAB.advance(progress,60)
assert(GEN.available_definitions().has(LAB.definition(id)))
assert(progress.laboratory_offers.is_empty() and progress.laboratory_unlocks.size()==LAB.IDS.size())
assert(GEN.available_definitions().size()==GEN.DEFINITIONS.size()+3)
var inventory: ShipInventory = player.get_node("ShipInventory")
var cannon := ItemInstance.new()
cannon.definition = LAB.definition("artillery_cannon")
var controller := StraightLaser.new()
player.add_child(controller)
controller.set_physics_process(false)
inventory.ammunition = 9
assert(not controller._consume_shot_resource(cannon) and inventory.ammunition==9)
inventory.ammunition = 10
assert(controller._consume_shot_resource(cannon) and inventory.ammunition==0)
assert(cannon.get_fire_interval()==2.2 and cannon.get_damage_type()==DamageTypes.Type.KINETIC)
var camera_offset: Vector2 = player.get_node("Camera2D").offset
controller._fire_projectile(cannon)
assert(preload("res://scripts/game/artillery_effect.gd")._sound.get_length()>0.1)
inventory.unlocked_weapon_slots = 3
var incendiary := ItemInstance.new()
incendiary.definition = LAB.definition("incendiary_missile")
inventory.weapon_slots[2] = incendiary
inventory.missile_ammunition = 2
assert(controller.try_fire_missile(2) and inventory.missile_ammunition==1)
assert(not controller.try_fire_missile(2) and inventory.missile_ammunition==1,"Incendiary uses shared missile cooldown")
var launched := false
for child in player.get_parent().get_children():
if child.get_script() == preload("res://scripts/game/weapon_projectile.gd"):
child.set_physics_process(false)
if child.weapon_effect == "incendiary":
assert(child.missile_homing and child.get_damage_type()==DamageTypes.Type.KINETIC)
launched = true
assert(launched,"Incendiary launcher creates a homing missile")
player.position = Vector2.ZERO
var target := target_at(Vector2(200,-28))
var laser := ItemInstance.new()
laser.definition = LAB.definition("radiation_laser")
laser.damage_min = 4
laser.damage_max = 4
inventory.weapon_slots[0] = laser
player.energy = player.max_energy
await get_tree().physics_frame
await get_tree().physics_frame
controller._fire_laser(0,laser)
assert(controller._laser_lines[0].radiation_beam and target.hull==9996)
var status := target.get_node("RadiationStatus")
status.set_physics_process(false)
status._physics_process(0.5)
assert(target.hull==9994,"Green laser deals radiation over time")
status.remaining = 0
var missile := preload("res://scenes/game/weapon_projectile.tscn").instantiate()
missile.weapon_effect = "incendiary"
missile.configure(target.position,Vector2.RIGHT,ItemDefinition.WeaponKind.MISSILE,400,20,1000)
add_child(missile)
missile.set_physics_process(false)
var hp: float = target.hull
missile._hit_target(target)
assert(target.hull==hp-20)
await get_tree().process_frame
var clouds := get_tree().get_nodes_in_group("burning_gas")
assert(clouds.size()==1)
var gas: Node2D = clouds[0]
gas.set_physics_process(false)
gas.remaining = 5
gas._tick_elapsed = 0
var overlap := Node2D.new()
overlap.set_script(preload("res://scripts/game/burning_gas.gd"))
overlap.position = gas.position
overlap.damage_per_second = 10
add_child(overlap)
overlap.set_physics_process(false)
hp = target.hull
gas._physics_process(1)
overlap._physics_process(1)
assert(is_equal_approx(target.hull,hp-22),"Overlapping clouds use only strongest damage")
target.position += Vector2(200,0)
hp = target.hull
gas._physics_process(1)
assert(target.hull==hp,"Leaving the cloud stops gas damage")
gas._physics_process(10)
assert(gas.is_queued_for_deletion(),"Cloud expires after five seconds")
await get_tree().create_timer(0.22).timeout
assert(player.get_node("Camera2D").offset.is_equal_approx(camera_offset),"Artillery camera impulse restores the original offset")
print("LABORATORY_TEST: PASS (3 persistent choices, costs, gameplay clock, pause, save, unlock-only loot pool, artillery 10 ammo, green radiation laser, homing incendiary impact and non-stacking 5s gas, camera recovery)")