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() GameState.captain.level = 30 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") progress.analysis_samples = 10 assert(preload("res://scripts/planets/alien_research.gd").purchase(progress,"general")) GameState.advance_play_time(570.0,game) assert(progress.laboratory_remaining==30,"Research advances while docked without any mining stations") assert(progress.alien_remaining==30,"Both research stations advance in parallel") 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") progress.freighter_rooms.expansion_2 = "alien_research" manager.screen._select_section("expansion_2") await snapshot("res://artifacts/alien_research_progress.png") manager._leave() get_tree().paused = true GameState.advance_play_time(30,game) assert(progress.laboratory_remaining==30,"Ordinary pause stops research") assert(progress.alien_remaining==30,"Ordinary pause stops alien 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(progress.alien_skills.general) 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,600) 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)")