diff --git a/CaptureLabel.gd b/CaptureLabel.gd index d3f6ef3..c52f8fb 100644 --- a/CaptureLabel.gd +++ b/CaptureLabel.gd @@ -5,12 +5,6 @@ extends Label # entirely when the current planet has no capture points (e.g. no setup # resolved for it) rather than showing "0/0". -# A direct script preload, not the bare "PlanetDefinitions" global class_name -# - the same load-order hazard PlanetSurfaceController's setup_host comment -# already documents: the global identifier is only guaranteed registered -# after an editor/import pass, a direct preload never depends on that. -const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") - var _was_conquered: bool = false @@ -24,14 +18,7 @@ func _process(_delta: float) -> void: var conquered: bool = GameState.is_planet_conquered(planet_id) if conquered: - # #76: a research_lab's whole reward is invisible otherwise - the - # research point balance lives in a HUD number the player has no - # reason to be watching at the exact moment it changes. Naming the - # reward right on the same flash that already announces conquest - # is the same "explain it where it happens" idiom BuildController - # uses for a refused build. - text = "CONQUERED - +1 RESEARCH POINT" \ - if GameState.planet_definition_id == PLANET_DEFINITIONS.RESEARCH_LAB_ID else "CONQUERED" + text = "CONQUERED" if not _was_conquered: _flash() else: diff --git a/EnemyArtilleryComponent.gd b/EnemyArtilleryComponent.gd index 9832905..43250c3 100644 --- a/EnemyArtilleryComponent.gd +++ b/EnemyArtilleryComponent.gd @@ -56,11 +56,6 @@ func _drift(enemy: Node2D, delta: float) -> void: if fleet == null or fleet.ships.is_empty(): return var to_fleet: Vector2 = fleet.get_centroid() - enemy.position - # #69: gated by the same fire_range it already shells within, rather - # than a second tunable that could drift out of sync - outside its own - # firing envelope it has nothing to be drifting toward yet. - if to_fleet.length() > fire_range: - return if to_fleet.length() < 1.0: return enemy.position += to_fleet.normalized() * drift_speed * delta diff --git a/EnemyPursuitComponent.gd b/EnemyPursuitComponent.gd index 6f7ae45..661f432 100644 --- a/EnemyPursuitComponent.gd +++ b/EnemyPursuitComponent.gd @@ -7,20 +7,11 @@ extends Node var capability_id: String = "pursuit" var velocity: Vector2 = Vector2.ZERO -# #69: used to beeline from anywhere on the map the instant it spawned, -# which read as omniscient rather than alert. Idle until the fleet actually -# comes within this radius. A persistent stalking threat wants the opposite -# of this - see EnemyPursuitComponentMothership.tscn, which overrides it to -# an effectively unlimited value for the mothership's own instance. -@export var detection_range: float = 850.0 - func tick(delta: float) -> void: var enemy: Node2D = get_parent() - var target: Node2D = _nearest_ship(enemy) - if target == null or enemy.position.distance_to(target.position) > detection_range: - return - var to_target: Vector2 = target.position - enemy.position + var target_pos: Vector2 = _nearest_ship_position(enemy) + var to_target: Vector2 = target_pos - enemy.position if to_target.length() > 1.0: var desired_velocity: Vector2 = to_target.normalized() * enemy.speed @@ -32,10 +23,10 @@ func tick(delta: float) -> void: enemy.rotation = lerp_angle(enemy.rotation, target_angle, enemy.rotation_speed * delta) -func _nearest_ship(enemy: Node2D) -> Node2D: +func _nearest_ship_position(enemy: Node2D) -> Vector2: var fleet: Node2D = get_tree().get_first_node_in_group("player_fleet") if fleet == null or fleet.ships.is_empty(): - return null + return enemy.position var nearest: Node2D = fleet.ships[0] var best_dist: float = enemy.position.distance_to(nearest.position) @@ -44,4 +35,4 @@ func _nearest_ship(enemy: Node2D) -> Node2D: if d < best_dist: best_dist = d nearest = ship - return nearest + return nearest.position diff --git a/EnemyPursuitComponentMothership.tscn b/EnemyPursuitComponentMothership.tscn deleted file mode 100644 index 108610d..0000000 --- a/EnemyPursuitComponentMothership.tscn +++ /dev/null @@ -1,7 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://EnemyPursuitComponent.gd" id="1"] - -[node name="EnemyPursuitComponentMothership" type="Node"] -script = ExtResource("1") -detection_range = 1000000.0 diff --git a/EnemyStandoffComponent.gd b/EnemyStandoffComponent.gd index 02e96a6..0111738 100644 --- a/EnemyStandoffComponent.gd +++ b/EnemyStandoffComponent.gd @@ -11,10 +11,6 @@ var capability_id: String = "standoff" @export var preferred_range: float = 620.0 @export var retreat_range_factor: float = 0.75 -# #69: kept comfortably beyond preferred_range so it still has room to close -# in before settling at stand-off distance, but the fleet has to actually be -# somewhere on the board first - see EnemyPursuitComponent's identical fix. -@export var detection_range: float = 1200.0 var velocity: Vector2 = Vector2.ZERO @@ -26,7 +22,7 @@ func tick(delta: float) -> void: return var to_target: Vector2 = target.position - enemy.position var distance: float = to_target.length() - if distance < 1.0 or distance > detection_range: + if distance < 1.0: return var direction: Vector2 = to_target / distance var retreat_range: float = preferred_range * retreat_range_factor diff --git a/GameController.gd b/GameController.gd index b0659c7..957914b 100644 --- a/GameController.gd +++ b/GameController.gd @@ -42,28 +42,8 @@ var pinch_start_zoom: float = 1.0 # release, so tapping still produces a clear, visible nudge. const PIVOT_SPEED: float = 1.6 const PIVOT_MIN_STEP: float = PI / 10.0 -# #72: the tap top-up below used to fire in full on EVERY release, with no -# regard for how recently the last one landed - so tapping fast enough -# granted rotation far above PIVOT_SPEED's smooth rate, well beyond what -# holding the button can ever produce. Each of those artificially-frequent -# re-seats (Fleet.apply_formation()) forces every ship into a fresh -# max-speed catch-up dash toward its new slot with no time to settle -# between them, and since outer-wedge ships swing further per pivot than -# inner ones, spamming made them visibly diverge in speed ("desync") while -# each individual ship's own bounded max_speed made the fleet as a whole -# read as constantly at its speed ceiling ("build up speed"). This cooldown -# is exactly the time a genuine hold needs to reach PIVOT_MIN_STEP on its -# own, so tap-spam can't out-pace holding the button down. -const PIVOT_TAP_COOLDOWN: float = PIVOT_MIN_STEP / PIVOT_SPEED var pivot_hold_amount: float = 0.0 -# Tracked PER DIRECTION, not shared - a single quick correction (tap the -# other way right after an overshoot) is normal play, not the spam this -# cooldown targets, and Q/E each independently producing their own minimum -# step back-to-back is an existing, tested guarantee (test_keyboard_input.gd). -# Only repeatedly re-tapping the SAME direction needs to be capped. -var pivot_cw_tap_cooldown_left: float = 0.0 -var pivot_ccw_tap_cooldown_left: float = 0.0 # Touch index currently holding each pivot button, or -1. These buttons are # driven straight from raw touch events rather than through _gui_input, # because Godot's Control GUI treats touch as a single emulated pointer and @@ -206,16 +186,9 @@ func _update_pivot(delta: float) -> void: func _finish_pivot_tap(direction: float) -> void: # Fired on release. A press held long enough has already turned far - # enough on its own; a brief tap gets topped up to the minimum step - - # but only once THAT DIRECTION's own cooldown has cleared, so spamming - # one side can't grant rotation faster than holding the button would. - var cooldown_left: float = pivot_cw_tap_cooldown_left if direction > 0.0 else pivot_ccw_tap_cooldown_left - if pivot_hold_amount < PIVOT_MIN_STEP and cooldown_left <= 0.0: + # enough on its own; a brief tap gets topped up to the minimum step. + if pivot_hold_amount < PIVOT_MIN_STEP: _pivot((PIVOT_MIN_STEP - pivot_hold_amount) * direction) - if direction > 0.0: - pivot_cw_tap_cooldown_left = PIVOT_TAP_COOLDOWN - else: - pivot_ccw_tap_cooldown_left = PIVOT_TAP_COOLDOWN pivot_hold_amount = 0.0 @@ -238,8 +211,6 @@ func _on_joystick_direction(direction: Vector2) -> void: func _process(delta: float) -> void: - pivot_cw_tap_cooldown_left = maxf(pivot_cw_tap_cooldown_left - delta, 0.0) - pivot_ccw_tap_cooldown_left = maxf(pivot_ccw_tap_cooldown_left - delta, 0.0) _update_pivot(delta) # Keep the heading fresh while a control is held. Fleet advances its own diff --git a/GameState.gd b/GameState.gd index c41a18b..35c4bbc 100644 --- a/GameState.gd +++ b/GameState.gd @@ -329,19 +329,13 @@ const SKILL_TREE: Array = [ "desc": "Permanent: +1 vessel in the fleet.", }, { - # #70: was x=0.50 - the same lane freight's branch already occupies at - # this same depth (power_systems), so the two nodes were drawn - # directly on top of each other. Kept in line with wing (its own - # prerequisite, also x=0.82) instead, so the whole fleet-size - # doctrine branch now runs straight rather than zigzagging into - # someone else's lane. "id": "squadron", "name": "Squadron Doctrine", "cost": 70, "requires": "wing", - "x": 0.82, "fleet_bonus": 1, "research_points": 2, + "x": 0.50, "fleet_bonus": 1, "research_points": 2, "desc": "Permanent: +1 vessel in the fleet.", }, { "id": "armada", "name": "Armada Doctrine", "cost": 150, "requires": "squadron", - "x": 0.82, "fleet_bonus": 1, "research_points": 3, + "x": 0.50, "fleet_bonus": 1, "research_points": 3, "desc": "Permanent: +1 vessel in the fleet.", }, ] diff --git a/InterceptorSpeedUpgrade.tres b/InterceptorSpeedUpgrade.tres new file mode 100644 index 0000000..16ac1fe --- /dev/null +++ b/InterceptorSpeedUpgrade.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="VesselUpgradeDefinition" load_steps=2 format=3] +[ext_resource type="Script" path="res://VesselUpgradeDefinition.gd" id="1"] +[resource] +script = ExtResource("1") +upgrade_id = "int_speed" +display_name = "Thrusters" +description = "+40 top speed, +60 acceleration per level." +base_cost = 25 +max_level = 3 +research_point_cost = 1 +icon = 18 +additive_modifiers = {"max_speed": 40.0, "acceleration": 60.0} diff --git a/InterceptorVesselDefinition.tres b/InterceptorVesselDefinition.tres index 1db52b7..f0330e5 100644 --- a/InterceptorVesselDefinition.tres +++ b/InterceptorVesselDefinition.tres @@ -1,9 +1,10 @@ -[gd_resource type="Resource" script_class="VesselDefinition" load_steps=7 format=3] +[gd_resource type="Resource" script_class="VesselDefinition" load_steps=8 format=3] [ext_resource type="Script" path="res://VesselDefinition.gd" id="1"] [ext_resource type="PackedScene" path="res://VesselWeaponComponent.tscn" id="2"] [ext_resource type="Resource" path="res://InterceptorDamageUpgrade.tres" id="3"] [ext_resource type="Resource" path="res://InterceptorRateUpgrade.tres" id="4"] [ext_resource type="Resource" path="res://InterceptorRangeUpgrade.tres" id="5"] +[ext_resource type="Resource" path="res://InterceptorSpeedUpgrade.tres" id="6"] [ext_resource type="PackedScene" path="res://VesselCargoComponent.tscn" id="7"] [resource] script = ExtResource("1") @@ -18,4 +19,4 @@ fleet_eligible = true capabilities = PackedStringArray("weapon", "cargo") component_scenes = Array[PackedScene]([ExtResource("2"), ExtResource("7")]) base_stats = {"damage": 1.0, "fire_cooldown": 0.5, "fire_range": 320.0, "max_speed": 460.0, "acceleration": 450.0, "delivery_speed": 180.0, "cargo_capacity": 20.0} -upgrades = Array[Resource]([ExtResource("3"), ExtResource("4"), ExtResource("5")]) +upgrades = Array[Resource]([ExtResource("3"), ExtResource("4"), ExtResource("5"), ExtResource("6")]) diff --git a/Main.tscn b/Main.tscn index 536b8f5..e7a1427 100644 --- a/Main.tscn +++ b/Main.tscn @@ -227,12 +227,10 @@ visible = false mouse_filter = 2 anchor_left = 1.0 anchor_right = 1.0 -anchor_top = 1.0 -anchor_bottom = 1.0 offset_left = -94 offset_right = -24 -offset_top = -720 -offset_bottom = -560 +offset_top = 740 +offset_bottom = 920 script = ExtResource("31") [node name="Label" type="Label" parent="UI/SpeedFader"] @@ -1297,12 +1295,11 @@ text = "" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = -310 -offset_right = -230 +offset_left = -330 +offset_right = -210 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Interceptor" @@ -1310,12 +1307,11 @@ text = "Interceptor" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = -220 -offset_right = -140 +offset_left = -240 +offset_right = -120 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Shield" @@ -1323,12 +1319,11 @@ text = "Shield" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = -130 -offset_right = -50 +offset_left = -150 +offset_right = -30 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Gas Bomber" @@ -1336,12 +1331,11 @@ text = "Gas Bomber" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = -40 -offset_right = 40 +offset_left = -60 +offset_right = 60 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Transporter" @@ -1349,12 +1343,11 @@ text = "Transporter" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = 50 -offset_right = 130 +offset_left = 30 +offset_right = 150 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Carrier" @@ -1362,12 +1355,11 @@ text = "Carrier" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = 140 -offset_right = 220 +offset_left = 120 +offset_right = 240 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Laser Ship" @@ -1375,12 +1367,11 @@ text = "Laser Ship" mouse_filter = 2 anchor_left = 0.5 anchor_right = 0.5 -offset_left = 230 -offset_right = 310 +offset_left = 210 +offset_right = 330 offset_top = 400 -offset_bottom = 472 -theme_override_font_sizes/font_size = 15 -autowrap_mode = 2 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 horizontal_alignment = 1 text = "Distorder" @@ -1388,9 +1379,9 @@ text = "Distorder" mouse_filter = 2 anchor_right = 1.0 offset_left = 48 -offset_top = 480 +offset_top = 462 offset_right = -48 -offset_bottom = 484 +offset_bottom = 466 color = Color(0.25, 0.85, 1, 0.6) [node name="UpgradeList" type="Label" parent="UI/LabMenu"] @@ -1399,7 +1390,7 @@ mouse_filter = 2 anchor_right = 1.0 anchor_bottom = 1.0 offset_left = 32 -offset_top = 488 +offset_top = 470 offset_right = -32 offset_bottom = -470 theme_override_font_sizes/font_size = 30 @@ -1481,8 +1472,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = -240 offset_right = -120 -offset_top = 488 -offset_bottom = 598 +offset_top = 470 +offset_bottom = 580 script = ExtResource("10") icon = 13 glow_color = Color(0.25, 0.85, 1, 1) @@ -1493,8 +1484,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = -310 offset_right = -50 -offset_top = 603 -offset_bottom = 673 +offset_top = 585 +offset_bottom = 655 theme_override_font_sizes/font_size = 22 autowrap_mode = 2 horizontal_alignment = 1 @@ -1505,8 +1496,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = 120 offset_right = 240 -offset_top = 488 -offset_bottom = 598 +offset_top = 470 +offset_bottom = 580 script = ExtResource("10") icon = 13 glow_color = Color(0.25, 0.85, 1, 1) @@ -1517,8 +1508,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = 50 offset_right = 310 -offset_top = 603 -offset_bottom = 673 +offset_top = 585 +offset_bottom = 655 theme_override_font_sizes/font_size = 22 autowrap_mode = 2 horizontal_alignment = 1 @@ -1529,8 +1520,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = -240 offset_right = -120 -offset_top = 678 -offset_bottom = 783 +offset_top = 660 +offset_bottom = 765 script = ExtResource("10") icon = 13 glow_color = Color(0.25, 0.85, 1, 1) @@ -1541,8 +1532,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = -310 offset_right = -50 -offset_top = 788 -offset_bottom = 833 +offset_top = 770 +offset_bottom = 815 theme_override_font_sizes/font_size = 22 autowrap_mode = 2 horizontal_alignment = 1 @@ -1553,8 +1544,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = 120 offset_right = 240 -offset_top = 678 -offset_bottom = 783 +offset_top = 660 +offset_bottom = 765 script = ExtResource("10") icon = 13 glow_color = Color(0.25, 0.85, 1, 1) @@ -1565,8 +1556,8 @@ anchor_left = 0.5 anchor_right = 0.5 offset_left = 50 offset_right = 310 -offset_top = 788 -offset_bottom = 833 +offset_top = 770 +offset_bottom = 815 theme_override_font_sizes/font_size = 22 autowrap_mode = 2 horizontal_alignment = 1 diff --git a/MothershipEnemyDefinition.tres b/MothershipEnemyDefinition.tres index f031799..cd03144 100644 --- a/MothershipEnemyDefinition.tres +++ b/MothershipEnemyDefinition.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" script_class="EnemyDefinition" load_steps=6 format=3] [ext_resource type="Script" path="res://EnemyDefinition.gd" id="1"] -[ext_resource type="PackedScene" path="res://EnemyPursuitComponentMothership.tscn" id="2"] +[ext_resource type="PackedScene" path="res://EnemyPursuitComponent.tscn" id="2"] [ext_resource type="PackedScene" path="res://EnemySpawnerComponentChaser.tscn" id="3"] [ext_resource type="PackedScene" path="res://EnemySpawnerComponentArtillery.tscn" id="4"] [ext_resource type="PackedScene" path="res://EnemyBigLaserComponent.tscn" id="5"] diff --git a/PlanetSetupEditor.gd b/PlanetSetupEditor.gd index 7d0f9fd..0f34b17 100644 --- a/PlanetSetupEditor.gd +++ b/PlanetSetupEditor.gd @@ -25,10 +25,6 @@ const MIN_ZOOM: float = 0.3 const MAX_ZOOM: float = 4.0 const WHEEL_ZOOM_STEP: float = 1.1 const HIT_RADIUS: float = 50.0 -# #65: how far a single-finger press has to move before it counts as a pan -# rather than a tap-to-place, once it's confirmed not to be starting on an -# existing marker. -const PAN_DEADZONE: float = 24.0 const ORIGIN_COLOR: Color = Color(1.0, 0.9, 0.4) # Duplicated from PaletteEntryButton.RING_COLOR rather than referenced # cross-class - a global class_name lookup at parse time needs the class @@ -49,22 +45,14 @@ const PALETTE_STEP: float = 92.0 @onready var load_button: IconButton = $UI/LoadButton @onready var delete_button: IconButton = $UI/DeleteButton @onready var status_label: Label = $UI/StatusLabel -@onready var selected_label: Label = $UI/SelectedLabel @onready var palette_row: Control = $UI/PaletteRow -@onready var load_panel: Control = $UI/LoadPanel -@onready var load_list: ItemList = $UI/LoadPanel/List -@onready var load_close_button: IconButton = $UI/LoadPanel/CloseButton var current_setup: Resource var selected_palette: String = "capture_point" var selected_item: Dictionary = {} var _profile_ids: Array = [] var _profile_cursor: int = 0 -# #64: replaced the old "Load cycles to the next saved setup" behaviour, -# which gave no way to see what existed or jump straight to one by name - -# kept only as the id list _load_list_setup_ids indexes into when the -# panel's own selection fires. -var _load_list_setup_ids: PackedStringArray = PackedStringArray() +var _load_cursor: int = -1 var _palette_buttons: Array = [] var _origin_marker: NeonPiece @@ -88,8 +76,6 @@ func _ready() -> void: load_button.pressed.connect(_on_load_pressed) delete_button.pressed.connect(_on_delete_pressed) name_input.text_changed.connect(_on_name_changed) - load_close_button.pressed.connect(func(): load_panel.visible = false) - load_list.item_selected.connect(_on_load_list_item_selected) _origin_marker = NeonPiece.create(item_host, Vector2.ZERO, ORIGIN_COLOR) var cross_size: float = 26.0 @@ -114,7 +100,6 @@ func new_setup(setup_id: String, display_name: String, profile_id: String) -> vo # fails at runtime, since the compiler here only sees current_setup as # a plain Resource and can't confirm the target is Array[Resource]. selected_item = {} - _refresh_selected_label() _profile_cursor = maxi(_profile_ids.find(profile_id), 0) if name_input != null: name_input.text = display_name @@ -131,7 +116,6 @@ func load_setup(setup_id: String) -> bool: # explicit save. current_setup = setup.duplicate(true) selected_item = {} - _refresh_selected_label() _profile_cursor = maxi(_profile_ids.find(current_setup.surface_profile_id), 0) if name_input != null: name_input.text = current_setup.display_name @@ -317,19 +301,8 @@ func _on_load_pressed() -> void: if setups.is_empty(): _set_status("No saved setups yet.") return - load_list.clear() - _load_list_setup_ids = PackedStringArray() - for setup in setups: - load_list.add_item("%s (%s)" % [setup.display_name, setup.surface_profile_id]) - _load_list_setup_ids.append(setup.setup_id) - load_panel.visible = true - - -func _on_load_list_item_selected(index: int) -> void: - if index < 0 or index >= _load_list_setup_ids.size(): - return - load_setup(_load_list_setup_ids[index]) - load_panel.visible = false + _load_cursor = (_load_cursor + 1) % setups.size() + load_setup(setups[_load_cursor].setup_id) func _on_delete_pressed() -> void: @@ -341,7 +314,6 @@ func _on_delete_pressed() -> void: else: remove_enemy(selected_item.index) selected_item = {} - _refresh_selected_label() func _on_name_changed(new_text: String) -> void: @@ -364,24 +336,6 @@ func _set_status(message: String) -> void: status_label.text = message -# #66: names the currently-selected marker so a tap actually confirms what -# got picked, instead of the player having to infer it from which dot on -# screen turned white. -func _refresh_selected_label() -> void: - if selected_label == null: - return - if selected_item.is_empty() or current_setup == null: - selected_label.text = "" - return - var index: int = int(selected_item.index) - if selected_item.kind == "capture": - selected_label.text = "Selected: Capture Point #%d" % (index + 1) - else: - var spawn: Resource = current_setup.enemy_spawns[index] - var definition: Resource = ENEMY_DEFINITIONS.get_definition(spawn.enemy_id) - selected_label.text = "Selected: %s #%d" % [definition.display_name, index + 1] - - # --- placement/selection helpers ---------------------------------------- func _place_selected(world_pos: Vector2) -> void: @@ -451,33 +405,26 @@ func _handle_touch(event: InputEventScreenTouch) -> void: var world_pos: Vector2 = _screen_to_world(event.position) var hit: Dictionary = _find_marker_near(world_pos) drag_touch_index = event.index - pan_start_screen = event.position - pan_start_camera = camera.position if not hit.is_empty(): selected_item = hit dragging_item = hit drag_mode = "item" _rebuild_markers() - _refresh_selected_label() + elif selected_palette != "": + _place_selected(world_pos) + drag_mode = "" else: - # #65: a press over empty canvas no longer commits to placing - # an item immediately - it might be the start of a pan. Which - # one it turns out to be is only known on release (still - # under PAN_DEADZONE = a tap = place) or once a drag actually - # crosses that deadzone (= a pan), same tap-vs-drag idiom - # GasBombController uses for its own AIM_DEADZONE. - dragging_item = {} - drag_mode = "pending" + drag_mode = "pan" + pan_start_screen = event.position + pan_start_camera = camera.position else: - if event.index == drag_touch_index: - if drag_mode == "pending": - _place_selected(_screen_to_world(event.position)) - drag_touch_index = -1 - drag_mode = "" - dragging_item = {} touches.erase(event.index) if touches.size() < 2: pinch_active = false + if event.index == drag_touch_index: + drag_touch_index = -1 + drag_mode = "" + dragging_item = {} func _handle_drag(event: InputEventScreenDrag) -> void: @@ -493,8 +440,6 @@ func _handle_drag(event: InputEventScreenDrag) -> void: if event.index != drag_touch_index: return - if drag_mode == "pending" and event.position.distance_to(pan_start_screen) >= PAN_DEADZONE: - drag_mode = "pan" match drag_mode: "item": _move_selected(_screen_to_world(event.position)) diff --git a/PlanetSetupEditor.tscn b/PlanetSetupEditor.tscn index 0d41a64..99e033d 100644 --- a/PlanetSetupEditor.tscn +++ b/PlanetSetupEditor.tscn @@ -170,17 +170,6 @@ theme_override_font_sizes/font_size = 20 autowrap_mode = 2 text = "" -[node name="SelectedLabel" type="Label" parent="UI"] -mouse_filter = 2 -offset_left = 20 -offset_top = 250 -offset_right = 696 -offset_bottom = 286 -theme_override_font_sizes/font_size = 22 -theme_override_colors/font_color = Color(1, 0.95, 0.9, 1) -autowrap_mode = 2 -text = "" - [node name="PaletteRow" type="Control" parent="UI"] anchor_top = 1.0 anchor_bottom = 1.0 @@ -188,42 +177,3 @@ offset_left = 20 offset_top = -110 offset_right = 696 offset_bottom = -20 - -[node name="LoadPanel" type="Control" parent="UI"] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="Backdrop" type="ColorRect" parent="UI/LoadPanel"] -anchor_right = 1.0 -anchor_bottom = 1.0 -color = Color(0.03, 0.03, 0.08, 0.94) - -[node name="TitleLabel" type="Label" parent="UI/LoadPanel"] -mouse_filter = 2 -anchor_right = 1.0 -offset_left = 24 -offset_top = 36 -offset_right = -24 -offset_bottom = 90 -theme_override_font_sizes/font_size = 34 -horizontal_alignment = 1 -text = "Load a setup" - -[node name="List" type="ItemList" parent="UI/LoadPanel"] -offset_left = 24 -offset_top = 110 -offset_right = 696 -offset_bottom = 1180 -theme_override_font_sizes/font_size = 24 - -[node name="CloseButton" type="Control" parent="UI/LoadPanel"] -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -92 -offset_top = 20 -offset_right = -20 -offset_bottom = 92 -script = ExtResource("4") -icon = 1 -glow_color = Color(0.9, 0.35, 0.35, 1) diff --git a/PlanetSurface.tscn b/PlanetSurface.tscn index 56e8577..709872e 100644 --- a/PlanetSurface.tscn +++ b/PlanetSurface.tscn @@ -163,12 +163,10 @@ visible = false mouse_filter = 2 anchor_left = 1.0 anchor_right = 1.0 -anchor_top = 1.0 -anchor_bottom = 1.0 offset_left = -94 offset_right = -24 -offset_top = -720 -offset_bottom = -560 +offset_top = 740 +offset_bottom = 920 script = ExtResource("17") glow_color = Color(0.5, 0.18, 0.6, 1) core_color = Color(0.02, 0.09, 0.15, 1) diff --git a/ShieldSpeedUpgrade.tres b/ShieldSpeedUpgrade.tres new file mode 100644 index 0000000..ef2b8a4 --- /dev/null +++ b/ShieldSpeedUpgrade.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="VesselUpgradeDefinition" load_steps=2 format=3] +[ext_resource type="Script" path="res://VesselUpgradeDefinition.gd" id="1"] +[resource] +script = ExtResource("1") +upgrade_id = "shd_speed" +display_name = "Thrusters" +description = "+40 top speed, +60 acceleration per level." +base_cost = 25 +max_level = 3 +research_point_cost = 1 +icon = 18 +additive_modifiers = {"max_speed": 40.0, "acceleration": 60.0} diff --git a/ShieldVesselDefinition.tres b/ShieldVesselDefinition.tres index 83b4220..0a99ba1 100644 --- a/ShieldVesselDefinition.tres +++ b/ShieldVesselDefinition.tres @@ -1,8 +1,9 @@ -[gd_resource type="Resource" script_class="VesselDefinition" load_steps=5 format=3] +[gd_resource type="Resource" script_class="VesselDefinition" load_steps=6 format=3] [ext_resource type="Script" path="res://VesselDefinition.gd" id="1"] [ext_resource type="PackedScene" path="res://VesselShieldComponent.tscn" id="3"] [ext_resource type="Resource" path="res://ShieldHitsUpgrade.tres" id="4"] [ext_resource type="Resource" path="res://ShieldRechargeUpgrade.tres" id="5"] +[ext_resource type="Resource" path="res://ShieldSpeedUpgrade.tres" id="6"] [resource] script = ExtResource("1") definition_id = "shield" @@ -18,7 +19,7 @@ component_scenes = Array[PackedScene]([ExtResource("3")]) hull_points = PackedVector2Array(0, -24, 20.7846, -12, 20.7846, 12, 0, 24, -20.7846, 12, -20.7846, -12) base_stats = {"damage": 1.0, "fire_cooldown": 0.5, "fire_range": 320.0, "max_speed": 460.0, "acceleration": 450.0, "shield_charges": 1.0, "shield_recharge": 6.0, "delivery_speed": 180.0} minimum_stats = {"shield_recharge": 2.0} -upgrades = Array[Resource]([ExtResource("4"), ExtResource("5")]) +upgrades = Array[Resource]([ExtResource("4"), ExtResource("5"), ExtResource("6")]) unlock_id = "unlock_shield" unlock_name = "Unlock Shield Vessel" unlock_description = "Lets the ship crafter build shield vessels. Its shield absorbs one lethal hit, then cycles back up." diff --git a/WorldGen.gd b/WorldGen.gd index 1fb1c58..b95837d 100644 --- a/WorldGen.gd +++ b/WorldGen.gd @@ -22,12 +22,10 @@ const GAS_GIANT_THRESHOLD: float = 0.55 # chance independent of biome - not a second smooth region to tune against # the first. const BLACK_HOLE_CHANCE: float = 0.05 -# #63/#74: findable without excessive exploration - research points gate -# mid/late skill-tree nodes and vessel research, so a player needs to run -# into one of these reasonably often, not just as a rare curiosity. Raised -# from 0.08 after playtesting found that rate too scarce for how central -# research points had become to progression. -const RESEARCH_LAB_CHANCE: float = 0.16 +# #63: findable without excessive exploration - research points gate mid/ +# late skill-tree nodes and vessel research, so a player needs to run into +# one of these reasonably often, not just as a rare curiosity. +const RESEARCH_LAB_CHANCE: float = 0.08 # A predictable early gas source for play-testing. The origin chunk is # deterministically normal space. Its explicit in-chunk position puts it diff --git a/tests/_lab_menu_preview.gd b/tests/_lab_menu_preview.gd index 80db449..d78c1b6 100644 --- a/tests/_lab_menu_preview.gd +++ b/tests/_lab_menu_preview.gd @@ -1,29 +1,37 @@ extends Node2D -# Preview, not a test - #71's ship lab layout with all 7 vessels present, -# checking the vessel-name row no longer overlaps itself. +# Preview, not a test. The ship lab's upgrade screen (#5), so its layout can +# be checked on a real viewport - headless draws no pixels. # # bash tests/screenshot.sh res://tests/_lab_menu_preview.tscn /tmp/lab.png 20 720x1280 -var configured: bool = false - -func _process(_delta: float) -> void: - if configured: - return - configured = true +func _ready() -> void: GameState.buildings.clear() GameState.extractors.clear() GameState.transporters.clear() - GameState.rocks = 500 - GameState.research_points = 5 + GameState.reset_progression() + GameState.rocks = 240 + GameState.gas = 36 + GameState.unlocked = ["shipyards", "vessel_science"] var main: Node = load("res://Main.tscn").instantiate() add_child(main) + main.get_node("BackgroundLayer").visible = false + main.get_node("Fleet").visible = false + main.get_node("AsteroidField").visible = false + main.get_node("PlanetField").visible = false + main.get_node("BuildingField").visible = false main.get_node("EnemySpawner").set_process(false) - main.get_node("AsteroidField").set_process(false) main.get_node("BuildingField").place(GameState.BUILDING_SHIP_LAB, Vector2(400.0, 0.0)) var lab_id: int = int(GameState.buildings[-1].id) + GameState.deposit_to_building(lab_id, 18) + GameState.fleet_cargo = 12 + GameState.fleet_gas_cargo = 8 var bc: Node = main.get_node("BuildController") - bc.lab_building_id = lab_id + # The shared header (#24) hides its HERE column when it has no building, + # so bind it the way opening the panel from the building does. + for bar in bc.resource_bars: + bar.building_id = lab_id main.get_node("UI/LabMenu").visible = true + bc.lab_vessel = GameState.VESSEL_GAS_BOMBER bc._refresh_lab() diff --git a/tests/_setup_editor_load_panel_preview.gd b/tests/_setup_editor_load_panel_preview.gd deleted file mode 100644 index 798d1b0..0000000 --- a/tests/_setup_editor_load_panel_preview.gd +++ /dev/null @@ -1,12 +0,0 @@ -extends Node2D - -# Preview, not a test - #64's Load panel: a real selectable list of saved -# setups instead of blind cycling. -# -# bash tests/screenshot.sh res://tests/_setup_editor_load_panel_preview.tscn /tmp/load.png 20 720x1280 - - -func _ready() -> void: - var editor: Node = load("res://PlanetSetupEditor.tscn").instantiate() - add_child(editor) - editor._on_load_pressed() diff --git a/tests/_setup_editor_load_panel_preview.tscn b/tests/_setup_editor_load_panel_preview.tscn deleted file mode 100644 index bc5137f..0000000 --- a/tests/_setup_editor_load_panel_preview.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://tests/_setup_editor_load_panel_preview.gd" id="1"] - -[node name="SetupEditorLoadPanelPreview" type="Node2D"] -script = ExtResource("1") diff --git a/tests/_setup_editor_preview.gd b/tests/_setup_editor_preview.gd index 4087ba8..2cc48dd 100644 --- a/tests/_setup_editor_preview.gd +++ b/tests/_setup_editor_preview.gd @@ -12,4 +12,3 @@ func _ready() -> void: editor.add_enemy("artillery", Vector2(0, 350)) editor.selected_item = {"kind": "capture", "index": 0} editor._rebuild_markers() - editor._refresh_selected_label() diff --git a/tests/test_enemy_detection_range.gd b/tests/test_enemy_detection_range.gd deleted file mode 100644 index 63f7e6e..0000000 --- a/tests/test_enemy_detection_range.gd +++ /dev/null @@ -1,129 +0,0 @@ -extends SceneTree - -# Headless gameplay test: run with -# godot --headless -s res://tests/test_enemy_detection_range.gd -# Forgejo #69: enemies used to beeline toward the fleet from anywhere on the -# map the instant they spawned. Chasers (EnemyPursuitComponent) and snipers -# (EnemyStandoffComponent) now sit idle until the fleet is within their own -# detection_range; the enemy mothership deliberately keeps an (effectively) -# unlimited range instead, since it is meant to read as a persistent, -# unavoidable stalking threat rather than a local sentry. -# -# Covers: -# A. a chaser beyond detection_range does not move at all -# B. the same chaser, once the fleet is within range, closes in -# C. a sniper beyond detection_range does not move either -# D. the enemy mothership closes in even from far beyond a chaser's range - -var game_state: Node -var main: Node -var fleet: Node -var chaser: Node -var sniper: Node -var mothership: Node -var chaser_start: Vector2 -var sniper_start: Vector2 -var mothership_start: Vector2 -var stage: int = 0 -var elapsed: float = 0.0 - - -func _initialize() -> void: - game_state = root.get_node("GameState") - game_state.buildings.clear() - game_state.extractors.clear() - game_state.transporters.clear() - game_state.reset_progression() - main = load("res://Main.tscn").instantiate() - root.add_child(main) - fleet = main.get_node("Fleet") - - -func _process(delta: float) -> bool: - elapsed += delta - match stage: - 0: - if elapsed < 0.2: - return false - main.get_node("EnemySpawner").set_process(false) - main.get_node("AsteroidField").set_process(false) - for a in get_nodes_in_group("asteroids"): - a.queue_free() - fleet.spawn_ships(1, Vector2.ZERO) - - # --- A. beyond detection_range: no movement at all --------------- - chaser = load("res://Enemy.tscn").instantiate() - chaser.enemy_id = "chaser" - var pursuit: Node = null - main.add_child(chaser) - pursuit = chaser.get_component("pursuit") - chaser.position = fleet.get_centroid() + Vector2(pursuit.detection_range + 300.0, 0.0) - chaser_start = chaser.position - - sniper = load("res://Enemy.tscn").instantiate() - sniper.enemy_id = "sniper" - main.add_child(sniper) - var standoff: Node = sniper.get_component("standoff") - sniper.position = fleet.get_centroid() + Vector2(0.0, standoff.detection_range + 300.0) - sniper_start = sniper.position - - elapsed = 0.0 - stage = 1 - return false - 1: - if elapsed < 1.0: - return false - var chaser_moved: float = chaser.position.distance_to(chaser_start) - var sniper_moved: float = sniper.position.distance_to(sniper_start) - print("beyond range for 1.0s: chaser moved %.1f, sniper moved %.1f" % [chaser_moved, sniper_moved]) - if chaser_moved > 1.0: - return _fail("a chaser beyond detection_range moved %.1f - it should sit idle" % chaser_moved) - if sniper_moved > 1.0: - return _fail("a sniper beyond detection_range moved %.1f - it should sit idle" % sniper_moved) - - # --- B. bring the chaser within range, it should now close in ---- - var pursuit: Node = chaser.get_component("pursuit") - chaser.position = fleet.get_centroid() + Vector2(pursuit.detection_range * 0.5, 0.0) - chaser_start = chaser.position - elapsed = 0.0 - stage = 2 - return false - 2: - if elapsed < 1.0: - return false - var chaser_moved: float = chaser.position.distance_to(chaser_start) - print("within range for 1.0s: chaser moved %.1f" % chaser_moved) - if chaser_moved < 20.0: - return _fail("a chaser within detection_range did not close in (moved %.1f)" % chaser_moved) - print("chaser correctly idle beyond range, closes in once inside it") - - # --- D. the mothership closes in from far beyond a chaser's range - - mothership = load("res://Enemy.tscn").instantiate() - mothership.enemy_id = "enemy_mothership" - main.add_child(mothership) - var mother_pursuit: Node = mothership.get_component("pursuit") - if mother_pursuit.detection_range < 50000.0: - return _fail("the mothership's pursuit range is not effectively unlimited (%f)" \ - % mother_pursuit.detection_range) - mothership.position = fleet.get_centroid() + Vector2(5000.0, 0.0) - mothership_start = mothership.position - elapsed = 0.0 - stage = 3 - return false - 3: - if elapsed < 1.0: - return false - var mothership_moved: float = mothership.position.distance_to(mothership_start) - print("mothership 5000 units out, 1.0s: moved %.1f" % mothership_moved) - if mothership_moved < 5.0: - return _fail("the mothership did not close in from far away (moved %.1f)" % mothership_moved) - print("TEST_ENEMY_DETECTION_RANGE: PASS") - quit(0) - return true - return false - - -func _fail(message: String) -> bool: - print("TEST_ENEMY_DETECTION_RANGE: FAIL (%s)" % message) - quit(1) - return true diff --git a/tests/test_extractor_building.gd b/tests/test_extractor_building.gd index 002046e..656c2c5 100644 --- a/tests/test_extractor_building.gd +++ b/tests/test_extractor_building.gd @@ -27,12 +27,7 @@ func _initialize() -> void: for x in range(-15, 15): for y in range(-15, 15): var chunk: Vector2i = Vector2i(x, y) - # #75: this test is about the planet-proximity/duplicate rules, - # not resource type - skip a research_lab specifically, since it - # yields nothing and is correctly refused (see - # test_research_lab_extractor_gate.gd for that behaviour). - if WorldGen.has_planet_at_chunk(chunk) \ - and WorldGen.planet_data_at_chunk(chunk).definition_id != "research_lab": + if WorldGen.has_planet_at_chunk(chunk): target_chunk = chunk found = true break diff --git a/tests/test_pivot_controls.gd b/tests/test_pivot_controls.gd index 1d60512..85ef2e9 100644 --- a/tests/test_pivot_controls.gd +++ b/tests/test_pivot_controls.gd @@ -5,10 +5,7 @@ extends SceneTree # Covers the fleet-pivot buttons: # 1. HOLDING one turns the formation continuously, not once per press # 2. a brief tap still produces a clear minimum step -# 3. immediately repeating a tap grants nothing extra until the cooldown -# clears (#72) - spamming taps must never rotate faster than holding -# the button would -# 4. pivoting works while the fleet is under way, i.e. the new layout +# 3. pivoting works while the fleet is under way, i.e. the new layout # survives the joystick's per-frame steering updates instead of being # overwritten by the old offsets @@ -79,17 +76,6 @@ func _process(delta: float) -> bool: quit(1) return true - # --- #72: spamming taps grants nothing extra during the cooldown --- - var before_spam: float = _angle() - for i in range(5): - controller._finish_pivot_tap(1.0) - var spammed: float = absf(angle_difference(before_spam, _angle())) - print("5 taps back-to-back -> turned %.4f rad (expected ~0, cooldown not yet cleared)" % spammed) - if spammed > 0.001: - print("TEST_PIVOT_CONTROLS: FAIL (tap-spam granted rotation during the cooldown)") - quit(1) - return true - # --- pivot WHILE the joystick is held ------------------------- # Two fingers at once. This is routed through the same raw-touch # path the real device uses, because Godot's Control GUI treats diff --git a/tests/test_planet_conquest_gate.gd b/tests/test_planet_conquest_gate.gd index 5002345..93b69ba 100644 --- a/tests/test_planet_conquest_gate.gd +++ b/tests/test_planet_conquest_gate.gd @@ -29,12 +29,7 @@ func _initialize() -> void: for x in range(-15, 15): for y in range(-15, 15): var chunk: Vector2i = Vector2i(x, y) - # #75: this test is about the conquest gate, not resource type - - # skip a research_lab specifically, since it yields nothing and - # is correctly refused regardless of conquest (see - # test_research_lab_extractor_gate.gd for that behaviour). - if WorldGen.has_planet_at_chunk(chunk) \ - and WorldGen.planet_data_at_chunk(chunk).definition_id != "research_lab": + if WorldGen.has_planet_at_chunk(chunk): target_chunk = chunk found = true break diff --git a/tests/test_research_lab_extractor_gate.gd b/tests/test_research_lab_extractor_gate.gd deleted file mode 100644 index 98cf839..0000000 --- a/tests/test_research_lab_extractor_gate.gd +++ /dev/null @@ -1,114 +0,0 @@ -extends SceneTree - -# Headless gameplay test: run with -# godot --headless -s res://tests/test_research_lab_extractor_gate.gd -# Forgejo #75: a research_lab planet yields no resources at all (#63) - an -# extractor placed there would sit on an empty resource id forever. The -# build menu must refuse the placement and explain why, the same "explain -# the refusal" idiom used for every other build gate. Mirrors -# test_extractor_building.gd's own chunk-discovery/settle/confirm shape. - -var main_scene: Node -var fleet: Node -var game_state: Node -var controller: Node -var target_chunk: Vector2i -var target_planet_id: String -var stage: int = 0 -var elapsed: float = 0.0 - - -func _initialize() -> void: - var found: bool = false - for x in range(-40, 40): - for y in range(-40, 40): - var chunk: Vector2i = Vector2i(x, y) - if not WorldGen.has_planet_at_chunk(chunk): - continue - if WorldGen.planet_data_at_chunk(chunk).definition_id == "research_lab": - target_chunk = chunk - found = true - break - if found: - break - if not found: - print("TEST_RESEARCH_LAB_EXTRACTOR_GATE: FAIL (no research_lab chunk found to test with)") - quit(1) - return - - game_state = root.get_node("GameState") - game_state.rocks = 200 - game_state.buildings.clear() - game_state.extractors.clear() - game_state.planet_capture.clear() - target_planet_id = "chunk:%d:%d" % [target_chunk.x, target_chunk.y] - # This test is about the resource-id gate, not conquest itself (see - # test_planet_conquest_gate) - pre-conquer the target directly. - game_state.register_capture_points(target_planet_id, 1, "research_lab") - game_state.mark_point_captured(target_planet_id, 0) - game_state.reset_progression() - for node in GameState.SKILL_TREE: - game_state.unlocked.append(node.id) - - main_scene = load("res://Main.tscn").instantiate() - root.add_child(main_scene) - fleet = main_scene.get_node("Fleet") - controller = main_scene.get_node("BuildController") - - -func _process(delta: float) -> bool: - elapsed += delta - match stage: - 0: - main_scene.get_node("AsteroidField").set_process(false) - main_scene.get_node("EnemySpawner").set_process(false) - - var near: Vector2 = WorldGen.chunk_origin(target_chunk) + Vector2.ONE * WorldGen.CHUNK_SIZE * 0.5 - for ship in fleet.ships: - ship.position = near - fleet.move_to(near) - elapsed = 0.0 - stage = 1 - return false - 1: - var planet_field: Node = main_scene.get_node("PlanetField") - if elapsed < planet_field.check_interval + 0.2: - return false - if not planet_field.populated_chunks.has(target_chunk): - return _fail("the research_lab planet never spawned") - var planet: Node2D = planet_field.populated_chunks[target_chunk][0] - if String(planet.resource_type) != "": - return _fail("test setup error: chosen planet's resource_type is '%s', expected empty" \ - % planet.resource_type) - for ship in fleet.ships: - ship.position = planet.position - fleet.move_to(planet.position) - elapsed = 0.0 - stage = 2 - return false - 2: - if elapsed < 0.3: - return false - controller._on_build_pressed() - if not controller.build_menu.visible: - return _fail("the build menu did not open over the planet") - - var before: int = game_state.extractors.size() - controller._show_building_info(GameState.BUILDING_EXTRACTOR) - controller._confirm_build() - if game_state.extractors.size() != before: - return _fail("an extractor was built on a research_lab planet") - if not ("nothing to extract" in controller.build_status.text): - return _fail("refusal did not explain itself, status was '%s'" % controller.build_status.text) - print("extractor refused on a research_lab planet, explained: '%s'" % controller.build_status.text) - - print("TEST_RESEARCH_LAB_EXTRACTOR_GATE: PASS") - quit(0) - return true - return false - - -func _fail(message: String) -> bool: - print("TEST_RESEARCH_LAB_EXTRACTOR_GATE: FAIL (%s)" % message) - quit(1) - return true diff --git a/tests/test_ship_lab.gd b/tests/test_ship_lab.gd index 55a03f9..1342cce 100644 --- a/tests/test_ship_lab.gd +++ b/tests/test_ship_lab.gd @@ -113,10 +113,7 @@ func _process(delta: float) -> bool: quit(1) return true controller.call("_on_lab_vessel", 0) - # #73: the interceptor's Thrusters upgrade was removed (global - # fleet speed now belongs to the distorder fader, #59/#62) - # leaving 3 upgrade cards, not 4 - card 3 is correctly hidden now. - if not lab_card_0.visible or not lab_card_2.visible or lab_card_3.visible: + if not lab_card_0.visible or not lab_card_3.visible: print("TEST_SHIP_LAB: FAIL (interceptor upgrade cards were not shown)") quit(1) return true @@ -159,7 +156,7 @@ func _process(delta: float) -> bool: # --- 7. per vessel type -------------------------------------- var trn_speed_before: float = game_state.transporter_speed() - game_state.buy_upgrade("int_range") + game_state.buy_upgrade("int_speed") if game_state.transporter_speed() != trn_speed_before: print("TEST_SHIP_LAB: FAIL (an interceptor upgrade changed the transporter)") quit(1) diff --git a/tests/test_skill_tree.gd b/tests/test_skill_tree.gd index 0d3d9f6..e7dc526 100644 --- a/tests/test_skill_tree.gd +++ b/tests/test_skill_tree.gd @@ -29,24 +29,6 @@ var elapsed: float = 0.0 func _initialize() -> void: game_state = root.get_node("GameState") - - # --- #70: no two nodes drawn on top of each other ------------------- - # SkillTreeView.node_screen_pos() places a node purely from its own - # (depth, x) - two nodes sharing both values land on the exact same - # pixel with no visual way to tell them apart. depth is derived from - # `requires` chains, not authored directly, so this has to check the - # same way the view does rather than eyeballing the data. - var seen: Dictionary = {} - for node in GameState.SKILL_TREE: - var key: String = "%d:%s" % [game_state.skill_depth(node.id), node.x] - if seen.has(key): - print("TEST_SKILL_TREE: FAIL (%s and %s are both at depth/x %s - drawn on top of each other)" \ - % [seen[key], node.id, key]) - quit(1) - return - seen[key] = node.id - print("no two skill-tree nodes share the same (depth, x)") - game_state.rocks = 500 game_state.buildings.clear() game_state.extractors.clear() diff --git a/tests/test_space_time_distorder.gd b/tests/test_space_time_distorder.gd index 059321c..6890399 100644 --- a/tests/test_space_time_distorder.gd +++ b/tests/test_space_time_distorder.gd @@ -151,18 +151,13 @@ func _process(delta: float) -> bool: # "trailing", it just hasn't finished the same (time-invariant) # catch-up burst yet. Keep steering a while longer so that burst # is over before judging whether formation holds. - if elapsed < 2.5: + if elapsed < 1.5: return false # Allow the lag to scale with the boost (it inherently does, even # with both the anchor and ship speeds multiplied by the exact # same factor - see the note by unboosted_lag above) but not - # beyond that, with generous slack for measurement noise - this - # ran under a full-suite JOBS=1 sequential load once and strayed - # 77.7 against a 30.0 allowance (2.6x over) purely from device - # jitter, confirmed by an immediate clean pass in isolation - # straight afterward. A REAL formation-desync regression (#72) - # strays by orders of magnitude more than this margin allows. - var allowed_lag: float = maxf(unboosted_lag, 20.0) * game_state.fleet_speed_multiplier * 3.0 + # beyond that, with slack for measurement noise. + var allowed_lag: float = maxf(unboosted_lag, 20.0) * game_state.fleet_speed_multiplier * 1.5 for i in range(fleet.ships.size()): var ship: Node2D = fleet.ships[i] if not is_instance_valid(ship):