diff --git a/controllers/BuildController.gd b/controllers/BuildController.gd index 42cd7e6..c765963 100644 --- a/controllers/BuildController.gd +++ b/controllers/BuildController.gd @@ -63,7 +63,6 @@ var context_actions_expanded: bool = false func _ready() -> void: - get_viewport().size_changed.connect(_on_viewport_resized) _seat_hud_columns() build_button.pressed.connect(_on_build_pressed) action_button.pressed.connect(_on_context_action_pressed) @@ -98,22 +97,6 @@ func _ready() -> void: _layout_context_buttons() -# HudScale reapplies each control's authored offsets whenever the window -# changes, which wipes the two layouts this controller writes at runtime. -# Re-running them after a resize is what keeps a desktop window that gets -# dragged to another size from leaving the context row where it was. -func _on_viewport_resized() -> void: - # Deferred: HudScale reapplies on the same signal, and which of two - # siblings connected first is not a thing to depend on. By the end of the - # frame its offsets are in, and these two can seat against them. - _relayout_hud.call_deferred() - - -func _relayout_hud() -> void: - _seat_hud_columns() - _layout_context_buttons() - - func _seat_hud_columns() -> void: var readout: ResourceReadout = get_node_or_null("../UI/ResourceReadout") if readout == null: @@ -126,10 +109,7 @@ func _seat_hud_columns() -> void: highest = minf(highest, button.position.y) if is_inf(highest): return - # The margin is authored in the same canvas units as everything else, so - # it scales with the HUD (#228) or it would be the one gap that stayed - # phone-sized on a desktop. - var shift: float = readout.content_bottom() + HUD_COLUMN_MARGIN * readout.scale.y - highest + var shift: float = readout.content_bottom() + HUD_COLUMN_MARGIN - highest if shift <= 0.0: return for button in buttons: @@ -242,14 +222,7 @@ func _clear_detected_context() -> void: func _layout_context_buttons() -> void: - # position is in the UI layer's space and size is in the button's own, - # and since #228 those differ by the HUD scale on a short canvas. Every - # gap and stride below is therefore scaled, while the RECTS keep their - # authored size - HudScale renders them down rather than shrinking them. - # Mixing the two is what this function got wrong twice before (see - # test_hud_layout), and a desktop canvas makes it wrong again. - var k: float = build_button.scale.y - var row_top: float = build_button.position.y + build_button.size.y * k + HUD_ROW_GAP * k + var row_top: float = build_button.position.y + build_button.size.y + HUD_ROW_GAP var row_height: float = build_button.size.y action_button.offset_top = row_top action_button.offset_bottom = row_top + row_height @@ -261,8 +234,8 @@ func _layout_context_buttons() -> void: for i in range(buttons.size()): var button: Control = buttons[i] button.visible = true - button.offset_left = (188.0 + float(i) * 164.0) * k - button.offset_right = button.offset_left + 148.0 + button.offset_left = 188 + i * 164 + button.offset_right = button.offset_left + 148 button.offset_top = row_top button.offset_bottom = row_top + row_height diff --git a/core/GameState.gd b/core/GameState.gd index f804ecf..b8a8e5a 100644 --- a/core/GameState.gd +++ b/core/GameState.gd @@ -20,10 +20,6 @@ const CATALOG_CHECK = preload("res://core/CatalogCheck.gd") # survive the trip has to live here instead. var has_return_position: bool = false -# Set by the menu when a save has just been loaded, cleared by whoever acts -# on it - the same one-shot handoff as has_return_position above, and -# deliberately NOT saved: it describes this launch, not the run. -var resumed_from_save: bool = false var surviving_ship_count: int = STARTING_SHIP_COUNT var surviving_vessel_types: PackedStringArray = PackedStringArray() # #106: a mirror of what is flying, pushed by Fleet._sync_composition_state() diff --git a/planets/PlanetAtmosphereController.gd b/planets/PlanetAtmosphereController.gd index 7eb23e1..75a93fb 100644 --- a/planets/PlanetAtmosphereController.gd +++ b/planets/PlanetAtmosphereController.gd @@ -39,23 +39,6 @@ func _ready() -> void: camera.position = GameState.return_world_position camera.zoom = Vector2(GameState.return_camera_zoom, GameState.return_camera_zoom) GameState.has_return_position = false - elif GameState.resumed_from_save: - # #227: a resumed run used to fall through to the brand-new-game - # branch below, which put the STARTING fleet at the world origin - - # so loading a save stranded the player in empty space with the - # wrong ships while their base sat wherever they had left it. - # - # The mothership is where a wiped-out fleet is already rebuilt (see - # BuildController._check_fleet_wipe), so it is the run's home whether - # or not this is a resume. Falls back to the position the save was - # written at, and only then to the origin, because a run can have no - # mothership standing. - var home: Variant = GameState.get_mothership_position() - var at: Vector2 = home if home != null else GameState.fleet_position - fleet.spawn_ships(GameState.fleet_vessel_types.size(), at, - GameState.fleet_vessel_types) - camera.position = at - GameState.resumed_from_save = false elif GameState.intro_active: # #108: the prologue opens with a convoy, and crews it itself. This # node still runs first, so spawning the ordinary opening fleet here diff --git a/tests/test_hangar_management.gd b/tests/test_hangar_management.gd index 135e6c3..4233217 100644 --- a/tests/test_hangar_management.gd +++ b/tests/test_hangar_management.gd @@ -83,12 +83,8 @@ func _process(delta: float) -> bool: controller.transfer_button.visible = controller._has_transfer_context() controller._layout_context_buttons() var buttons: Array = [controller.building_action_button, controller.hangar_button] - # Rendered width, not the authored rect: #228 shrinks the HUD on a - # short canvas by scaling the node and leaving its rect alone, so - # offset_right overstates where a button actually ends. for i in range(buttons.size() - 1): - var ends: float = buttons[i].offset_left + buttons[i].size.x * buttons[i].scale.x - if ends > buttons[i + 1].offset_left: + if buttons[i].offset_right > buttons[i + 1].offset_left: _fail("context buttons %d and %d overlap" % [i, i + 1]) return true controller._collapse_context_actions() diff --git a/tests/test_hud_layout.gd b/tests/test_hud_layout.gd index 2fa4b8a..c6f7226 100644 --- a/tests/test_hud_layout.gd +++ b/tests/test_hud_layout.gd @@ -31,9 +31,6 @@ var controller: Node var readout: Node var stage: int = 0 var elapsed: float = 0.0 -# A desktop canvas (1080 units) and a phone one (3413), where the HUD -# scale is 0.45 and 1.0 respectively. -var shapes: Array = [Vector2i(1920, 1080), Vector2i(720, 1280)] # Deliberately share a row: a building either offers its own action or a # hangar, never both, so their rects coinciding is the design. @@ -55,40 +52,23 @@ func _process(delta: float) -> bool: return false scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) - # --- E. at BOTH shapes --------------------------------------- - # #228 shrinks the HUD on a short canvas, so every relationship - # here now has a scale in it. A desktop canvas is what the - # default viewport gives, and the phone is where that scale is - # 1.0 - the arithmetic has to hold at both or one of them is - # being checked by accident. - root.size = shapes.pop_front() - stage = 1 - return false - 1: - if elapsed < 0.2: - return false - elapsed = 0.0 - var label: String = "%.0f-unit canvas" % readout.get_viewport_rect().size.y - if not _check("as laid out at load, %s" % label): + if not _check("as laid out at load"): return true # --- C. the path that broke it ------------------------------ controller.context_actions_expanded = false controller._layout_context_buttons() - if not _check("collapsed, %s" % label): + if not _check("after _layout_context_buttons() collapsed"): return true controller.context_actions_expanded = true controller._layout_context_buttons() - if not _check("expanded, %s" % label): + if not _check("after _layout_context_buttons() expanded"): return true - if shapes.is_empty(): - print("TEST_HUD_LAYOUT: PASS") - quit(0) - return true - root.size = shapes.pop_front() - return false + print("TEST_HUD_LAYOUT: PASS") + quit(0) + return true return false @@ -130,21 +110,13 @@ func _check(context: String) -> bool: var b: Control = buttons[b_name] if a == null or b == null: continue - if _rendered(a).intersects(_rendered(b)): + if a.get_rect().intersects(b.get_rect()): return _fail("%s %s overlaps %s %s (%s)" \ - % [a_name, _rendered(a), b_name, _rendered(b), context]) + % [a_name, a.get_rect(), b_name, b.get_rect(), context]) print("clear of the readout (reaches %.0f) and of each other - %s" % [readout_bottom, context]) return true -# Where a control actually lands, which since #228 is not its layout rect: -# the HUD shrinks on a short canvas by scaling the node and leaving the rect -# at its authored size, so position is in scaled parent units while size is -# in unscaled local ones. -func _rendered(c: Control) -> Rect2: - return Rect2(c.position, c.size * c.scale) - - func _is_exclusive(a: String, b: String) -> bool: for pair in EXCLUSIVE_PAIRS: if (a == pair[0] and b == pair[1]) or (a == pair[1] and b == pair[0]): diff --git a/tests/test_hud_scales_to_screen.gd b/tests/test_hud_scales_to_screen.gd deleted file mode 100644 index 33b8d12..0000000 --- a/tests/test_hud_scales_to_screen.gd +++ /dev/null @@ -1,118 +0,0 @@ -extends SceneTree - -# Forgejo #228: the HUD must not be three times larger on a PC than on a -# phone. -# -# The layout is authored in CANVAS units. With stretch "canvas_items" and -# aspect "expand" over a 1920x1080 base, a 720x1280 phone scales by 0.375 and -# gets 1920x3413 canvas units, while ANY 16:9-or-wider screen lands on -# 1920x1080 units however many pixels it really has. The canvas is three -# times shorter, so an unscaled 148-unit button goes from 4.3% of the -# screen's height to 13.7% of it. -# -# Nothing collides - that was checked pair by pair on both canvases and the -# same six pairs overlap on each, all of them same-slot alternatives. The -# fault is purely that everything is enormous. -# -# Covers: -# A. on a phone the HUD is left exactly as authored -# B. on a desktop canvas a control takes a similar FRACTION of the screen -# rather than three times more -# C. the shrink is bounded, so a landscape phone keeps thumb-sized targets -# D. it follows a resize rather than only applying once at startup - -const PHONE: Vector2i = Vector2i(720, 1280) -const DESKTOP: Vector2i = Vector2i(1920, 1080) -const BIG_DESKTOP: Vector2i = Vector2i(2560, 1440) - -var scene: Node -var hud: Node -var button: Control -var frames: int = 0 -var stage: int = 0 -var phone_fraction: float = 0.0 - - -func _initialize() -> void: - var gs: Node = root.get_node("GameState") - gs.buildings.clear() - gs.reset_new_game() - gs.intro_active = false - scene = load("res://world/Main.tscn").instantiate() - root.add_child(scene) - current_scene = scene - hud = scene.get_node("UI/HudScale") - button = scene.get_node("UI/MapButton") - - -func _fraction() -> float: - # Height of a HUD control as a share of the screen's height - the number - # that actually differs between the two, and the one a player sees. - # Rendered height, not the layout rect: the rect keeps its authored size - # and the node is scaled, so scale is where the shrink actually lives. - return (button.size.y * button.scale.y) / button.get_viewport_rect().size.y - - -func _process(_delta: float) -> bool: - frames += 1 - if frames < 10: - return false - frames = 0 - match stage: - 0: - root.size = PHONE - stage = 1 - return false - 1: - # --- A. authored size on the shape it was authored for ------- - var k: float = hud.hud_scale() - if not is_equal_approx(k, 1.0): - return _fail("the phone HUD is being scaled by %.3f, but it is the authored shape" % k) - phone_fraction = _fraction() - print("phone %s: canvas %.0f tall, scale %.2f, control is %.1f%% of the screen" % [ - PHONE, button.get_viewport_rect().size.y, k, phone_fraction * 100.0]) - root.size = DESKTOP - stage = 2 - return false - 2: - # --- B. a similar share of a desktop screen ------------------ - var pc_fraction: float = _fraction() - var ratio: float = pc_fraction / maxf(phone_fraction, 0.0001) - print("desktop %s: canvas %.0f tall, scale %.2f, control is %.1f%% of the screen (%.2fx the phone)" % [ - DESKTOP, button.get_viewport_rect().size.y, hud.hud_scale(), - pc_fraction * 100.0, ratio]) - if ratio > 1.75: - return _fail("a HUD control is %.2fx its phone share on a desktop screen - that is the bug" % ratio) - # --- C. and not shrunk into uselessness ---------------------- - if hud.hud_scale() < 0.4: - return _fail("the HUD shrank to %.2f, past anything pressable" % hud.hud_scale()) - root.size = BIG_DESKTOP - stage = 3 - return false - 3: - # --- D. follows the resize ----------------------------------- - var big: float = _fraction() - print("big desktop %s: control is %.1f%% of the screen" % [BIG_DESKTOP, big * 100.0]) - if absf(big - phone_fraction) / maxf(phone_fraction, 0.0001) > 0.75: - return _fail("a bigger desktop screen did not keep the control proportional (%.1f%% vs %.1f%%)" \ - % [big * 100.0, phone_fraction * 100.0]) - # Back to the phone: the HUD must return, not stay shrunk. - root.size = PHONE - stage = 4 - return false - 4: - if not is_equal_approx(hud.hud_scale(), 1.0): - return _fail("returning to the phone shape left the HUD at %.3f" % hud.hud_scale()) - if absf(_fraction() - phone_fraction) > 0.001: - return _fail("the HUD did not come back to its authored size on resize") - print("resizing back to the phone restored the authored size") - print("TEST_HUD_SCALES_TO_SCREEN: PASS") - quit(0) - return true - return false - - -func _fail(message: String) -> bool: - print("TEST_HUD_SCALES_TO_SCREEN: FAIL (%s)" % message) - quit(1) - return true diff --git a/tests/test_resume_spawn.gd b/tests/test_resume_spawn.gd deleted file mode 100644 index f32a09b..0000000 --- a/tests/test_resume_spawn.gd +++ /dev/null @@ -1,150 +0,0 @@ -extends SceneTree - -# Forgejo #227: a resumed run comes back at its own mothership. -# -# Loading a save used to fall through to the brand-new-game branch in -# PlanetAtmosphereController, which spawns STARTING_SHIP_COUNT interceptors -# at the world origin. So a resume stranded the player in empty space with -# the wrong ships, while their base sat wherever they had left it - which on -# a charted map is thousands of units away. -# -# Covers: -# A. resuming puts the fleet at the mothership, not at the origin -# B. and crews it with the ships the save actually had -# C. with no mothership standing, it falls back to where the save was made -# D. a NEW game is untouched - still the starting fleet at the origin - -const HOME: Vector2 = Vector2(7400.0, -3100.0) -const AWAY: Vector2 = Vector2(-5200.0, 900.0) - -var game_state: Node -var scene: Node -var elapsed: float = 0.0 -var stage: int = 0 -var expected_crew: int = 0 - - -func _initialize() -> void: - game_state = root.get_node("GameState") - SaveGame.clear() - - -func _fresh_state() -> void: - game_state.buildings.clear() - game_state.extractors.clear() - game_state.reset_progression() - game_state.reset_new_game() - game_state.reset_quests() - game_state.intro_active = false - - -func _load_scene() -> void: - if scene != null and is_instance_valid(scene): - scene.free() - scene = load("res://world/Main.tscn").instantiate() - root.add_child(scene) - current_scene = scene - - -func _process(delta: float) -> bool: - elapsed += delta - match stage: - 0: - if elapsed < 0.3: - return false - # A run with a base at HOME and the fleet parked far from it. - _fresh_state() - game_state.add_building(GameState.BUILDING_MOTHERSHIP, HOME) - # Capacity for the crew below. Since #219 the fleet holds - # BASE_FLEET_SIZE (1) plus one berth per supplied fleet office, - # so a save with three ships flying is a save that had two - # offices standing - without them spawn_ships clamps to 1 and - # this would assert nothing. - game_state.add_building(GameState.BUILDING_FLEET_OFFICE, HOME + Vector2(400.0, 0.0)) - game_state.add_building(GameState.BUILDING_FLEET_OFFICE, HOME + Vector2(800.0, 0.0)) - game_state.fleet_vessel_types = PackedStringArray([ - GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_INTERCEPTOR, - GameState.VESSEL_INTERCEPTOR]) - game_state.fleet_position = AWAY - expected_crew = game_state.fleet_vessel_types.size() - if game_state.max_fleet_size() < expected_crew: - return _fail("the fleet cannot hold %d, so a resumed crew would prove nothing (cap %d)" \ - % [expected_crew, game_state.max_fleet_size()]) - if not SaveGame.save(game_state): - return _fail("could not write the save this test resumes from") - _fresh_state() - var result: Dictionary = SaveGame.load_into(game_state) - if not bool(result.get("ok", false)): - return _fail("the save would not load: %s" % result.get("reason", "")) - game_state.resumed_from_save = true - _load_scene() - elapsed = 0.0 - stage = 1 - return false - 1: - if elapsed < 0.6: - return false - var fleet: Node2D = scene.get_node("Fleet") - # --- A. at the mothership, not the origin ------------------- - var at: Vector2 = fleet.get_centroid() - if at.distance_to(HOME) > 400.0: - return _fail("resumed at %s, but the mothership is at %s" % [at, HOME]) - if at.distance_to(Vector2.ZERO) < 400.0: - return _fail("resumed at the world origin, which is the new-game behaviour") - print("resumed at the mothership (%.0f units from it)" % at.distance_to(HOME)) - - # --- B. with the crew the save had -------------------------- - if fleet.ships.size() != expected_crew: - return _fail("resumed with %d ships, the save had %d" \ - % [fleet.ships.size(), expected_crew]) - print("resumed with the saved crew of %d" % fleet.ships.size()) - if game_state.resumed_from_save: - return _fail("the resume flag was left raised, so the next scene would re-use it") - - # --- C. no mothership: fall back to where the save was made -- - _fresh_state() - game_state.fleet_vessel_types = PackedStringArray([GameState.VESSEL_INTERCEPTOR]) - game_state.fleet_position = AWAY - game_state.resumed_from_save = true - _load_scene() - elapsed = 0.0 - stage = 2 - return false - 2: - if elapsed < 0.6: - return false - var fleet2: Node2D = scene.get_node("Fleet") - var at2: Vector2 = fleet2.get_centroid() - if at2.distance_to(AWAY) > 400.0: - return _fail("with no mothership, resumed at %s rather than the saved %s" % [at2, AWAY]) - print("with no mothership, resumed where the save was written") - - # --- D. a new game is untouched ----------------------------- - _fresh_state() - game_state.resumed_from_save = false - _load_scene() - elapsed = 0.0 - stage = 3 - return false - 3: - if elapsed < 0.6: - return false - var fleet3: Node2D = scene.get_node("Fleet") - if fleet3.get_centroid().distance_to(Vector2.ZERO) > 400.0: - return _fail("a new game no longer starts at the origin (%s)" % fleet3.get_centroid()) - if fleet3.ships.size() != GameState.STARTING_SHIP_COUNT: - return _fail("a new game starts with %d ships, expected %d" \ - % [fleet3.ships.size(), GameState.STARTING_SHIP_COUNT]) - print("a new game still starts with %d ships at the origin" % fleet3.ships.size()) - - SaveGame.clear() - print("TEST_RESUME_SPAWN: PASS") - quit(0) - return true - return false - - -func _fail(message: String) -> bool: - print("TEST_RESUME_SPAWN: FAIL (%s)" % message) - quit(1) - return true diff --git a/ui/HudScale.gd b/ui/HudScale.gd deleted file mode 100644 index 5d306ba..0000000 --- a/ui/HudScale.gd +++ /dev/null @@ -1,78 +0,0 @@ -extends Node - -# Forgejo #228: the HUD is proportionally three times too big on a PC. -# -# The layout is authored in CANVAS units against a portrait phone. With -# stretch "canvas_items"/"expand" and a 1920x1080 base, a 720x1280 phone -# scales by 0.375 and so gets a canvas 1920x3413 units tall - while ANY -# 16:9-or-wider screen lands on 1920x1080 units whatever its pixel count. -# The canvas is three times shorter, so a 148-unit button goes from 4.3% of -# the screen's height to 13.7% of it. Nothing collides; everything is just -# enormous, and the thumb sticks in particular. -# -# Each HUD element's offsets are measured FROM ITS OWN ANCHOR, so scaling -# them scales the element's size and its distance from the edge it hangs off -# together. That is the whole trick: one multiplier per control, no layout -# rules to re-derive, and anchoring keeps working untouched. - -# The canvas the HUD was drawn for. Not a magic number: 1280 / (720 / 1920). -const AUTHORED_CANVAS_HEIGHT: float = 3413.0 - -# Below this the controls stop being worth pressing. A landscape phone gets -# the same short canvas as a desktop, and there the sticks are still thumb -# targets - so the shrink is bounded rather than purely proportional. -const MIN_SCALE: float = 0.45 - -# Authored offsets and sizes, captured before anything is touched. -# Re-reading them after a resize would measure a previous scaling and let the -# HUD ratchet away to nothing - the same trap NeonPanel documents. -var _authored: Dictionary = {} - - -func _ready() -> void: - var layer: Node = get_parent() - for child in layer.get_children(): - if child == self or not (child is Control): - continue - var c: Control = child - # Full-bleed things - the modal menus, the breach overlay - hang off - # every edge at once and have nothing to shrink. - if is_equal_approx(c.anchor_left, 0.0) and is_equal_approx(c.anchor_right, 1.0) \ - and is_equal_approx(c.anchor_top, 0.0) and is_equal_approx(c.anchor_bottom, 1.0): - continue - _authored[c] = [c.offset_left, c.offset_top, - c.offset_right - c.offset_left, c.offset_bottom - c.offset_top] - get_viewport().size_changed.connect(_apply) - _apply() - - -func hud_scale() -> float: - var height: float = get_viewport().get_visible_rect().size.y - if height <= 0.0: - return 1.0 - # Only ever shrinks. A canvas TALLER than the phone's is not a reason to - # inflate the HUD over the game. - return clampf(height / AUTHORED_CANVAS_HEIGHT, MIN_SCALE, 1.0) - - -func _apply() -> void: - var k: float = hud_scale() - for control in _authored: - if not is_instance_valid(control): - continue - var a: Array = _authored[control] - var c: Control = control - # Position moves toward the edge the element hangs off; the RECT keeps - # its authored size and the node is scaled instead. - # - # Scaling the rect alone was wrong: half the HUD draws its own - # contents at fixed canvas sizes - ResourceReadout lays its rows out - # in code - so a shrunken rect kept full-size content inside it, and - # the build button slid up underneath the resource list. Scaling the - # node takes the drawn content with it. - c.pivot_offset = Vector2.ZERO - c.offset_left = float(a[0]) * k - c.offset_top = float(a[1]) * k - c.offset_right = c.offset_left + float(a[2]) - c.offset_bottom = c.offset_top + float(a[3]) - c.scale = Vector2(k, k) diff --git a/ui/MainMenu.gd b/ui/MainMenu.gd index 214d83f..3d639c5 100644 --- a/ui/MainMenu.gd +++ b/ui/MainMenu.gd @@ -140,7 +140,6 @@ func _on_continue_pressed() -> void: # A resumed run never replays the prologue, whatever it was doing when # it was written. GameState.intro_active = false - GameState.resumed_from_save = true _start("res://world/Main.tscn") diff --git a/ui/ResourceReadout.gd b/ui/ResourceReadout.gd index 53fbe38..a2f6d97 100644 --- a/ui/ResourceReadout.gd +++ b/ui/ResourceReadout.gd @@ -79,13 +79,7 @@ func rows_height() -> float: # Bottom edge of the rows in this Control's parent space - the line anything # stacked below the readout has to clear. func content_bottom() -> float: - # Rendered, not laid out. #228 scales the HUD down on a short canvas by - # scaling the NODE while leaving its rect at the authored size, so - # rows_height() is in unscaled local units while position is already in - # scaled parent ones. Mixing the two overstated this by more than a - # button's height and pushed the columns down to clear space that was - # not occupied. - return position.y + rows_height() * scale.y + return position.y + rows_height() func _add_glyph(resource_type: String, at: Vector2) -> void: diff --git a/world/Main.tscn b/world/Main.tscn index 1d70f1b..31147f7 100644 --- a/world/Main.tscn +++ b/world/Main.tscn @@ -17,7 +17,6 @@ [ext_resource type="Script" path="res://world/Starfield.gd" id="1"] -[ext_resource type="Script" path="res://ui/HudScale.gd" id="hud_scale"] [ext_resource type="Script" path="res://vessels/Fleet.gd" id="2"] [ext_resource type="Script" path="res://controllers/GameController.gd" id="3"] [ext_resource type="Script" path="res://enemies/EnemySpawner.gd" id="4"] @@ -152,9 +151,6 @@ script = ExtResource("12") [node name="UI" type="CanvasLayer" parent="."] layer = 1 -[node name="HudScale" type="Node" parent="UI"] -script = ExtResource("hud_scale") - [node name="ResourceReadout" type="Control" parent="UI"] mouse_filter = 2 offset_left = 28