2026-08-15 14:43:46 +02:00
|
|
|
extends SceneTree
|
|
|
|
|
|
|
|
|
|
# Headless gameplay test: run with
|
|
|
|
|
# godot --headless -s res://tests/test_main_menu.gd
|
|
|
|
|
# Verifies:
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
# 1. the menu loads with Play/Map Editor/Quit; Map Editor is enabled and
|
|
|
|
|
# reaches PlanetSetupEditor.tscn (#48)
|
2026-08-15 14:43:46 +02:00
|
|
|
# 2. Quit's visibility follows OS.has_feature("web") (this platform is
|
|
|
|
|
# not a web export, so this run exercises the "visible" branch; the
|
|
|
|
|
# condition itself is what's under test, not the concrete boolean)
|
|
|
|
|
# 3. Play resets GameState to a fresh game AND reaches Main.tscn
|
|
|
|
|
|
|
|
|
|
var menu: Node
|
|
|
|
|
var game_state: Node
|
|
|
|
|
var elapsed: float = 0.0
|
|
|
|
|
var stage: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _initialize() -> void:
|
|
|
|
|
game_state = root.get_node("GameState")
|
|
|
|
|
menu = load("res://MainMenu.tscn").instantiate()
|
|
|
|
|
root.add_child(menu)
|
|
|
|
|
current_scene = menu
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
|
|
|
elapsed += delta
|
|
|
|
|
match stage:
|
|
|
|
|
0:
|
|
|
|
|
# A scene added to the tree inside _initialize() has not had its
|
|
|
|
|
# _ready() flushed yet - see test_planet_setups.gd for the same
|
|
|
|
|
# hazard - so the menu's @onready buttons aren't valid until a
|
|
|
|
|
# frame has actually processed.
|
|
|
|
|
if elapsed < 0.1:
|
|
|
|
|
return false
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
if not menu.editor_button.enabled:
|
|
|
|
|
return _fail("map editor button is not enabled (#48 shipped)")
|
2026-08-15 14:43:46 +02:00
|
|
|
if menu.quit_button.visible != (not OS.has_feature("web")):
|
|
|
|
|
return _fail("quit button visibility did not follow OS.has_feature('web')")
|
|
|
|
|
|
|
|
|
|
_press(menu.editor_button)
|
|
|
|
|
elapsed = 0.0
|
|
|
|
|
stage = 1
|
|
|
|
|
return false
|
|
|
|
|
1:
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
if elapsed < 0.2:
|
2026-08-15 14:43:46 +02:00
|
|
|
return false
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
var on_editor: bool = current_scene != null and current_scene.name == "PlanetSetupEditor"
|
|
|
|
|
if not on_editor:
|
|
|
|
|
return _fail("pressing Map Editor did not reach PlanetSetupEditor.tscn")
|
2026-08-15 14:43:46 +02:00
|
|
|
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
# Return to the menu the same way the player would, then test Play.
|
|
|
|
|
menu = load("res://MainMenu.tscn").instantiate()
|
|
|
|
|
root.add_child(menu)
|
|
|
|
|
current_scene = menu
|
|
|
|
|
elapsed = 0.0
|
|
|
|
|
stage = 2
|
|
|
|
|
return false
|
|
|
|
|
2:
|
|
|
|
|
if elapsed < 0.1:
|
|
|
|
|
return false
|
2026-08-15 14:43:46 +02:00
|
|
|
# Dirty the game state exactly like an in-progress run would,
|
|
|
|
|
# so a genuine reset is distinguishable from a coincidental match.
|
|
|
|
|
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 999)
|
|
|
|
|
game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
|
|
|
|
game_state.surviving_ship_count = 1
|
|
|
|
|
game_state.planet_capture["chunk:1:1"] = {"captured": [true], "conquered": true}
|
|
|
|
|
|
|
|
|
|
_press(menu.play_button)
|
|
|
|
|
elapsed = 0.0
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
stage = 3
|
2026-08-15 14:43:46 +02:00
|
|
|
return false
|
Add the planet setup map editor (#48)
PlanetSetupEditor authors PlanetSetup resources: pick a surface
profile (drives the real lunar_surface background, not a blank
sheet), place/move/delete capture points and enemies from a palette
generated straight off EnemyDefinitions.all() (a new enemy definition
shows up with no editor change), pan/pinch-zoom the canvas with the
same touch idiom as GameController, then save to
res://PlanetSetups/<id>.tres via ResourceSaver - reachable now from
the main menu's Map Editor button. The mutation methods
(new_setup/load_setup/add_*/move_*/remove_*/save_setup) are the whole
model and work standalone, which is what lets
tests/test_setup_editor.gd drive placement/move/delete/save/reload
directly without simulating touch input.
Every cross-file reference is a preloaded script const rather than a
bare global class name, matching #42/#44's load-order notes - this
file is instantiated from other scripts' function bodies, never as a
script-mode test entry point itself, so it wasn't at risk the same way
the test files were, but the pattern is kept consistent throughout.
Part of the #40 conquest-mechanic breakdown. This closes out the
breakdown: #41-#48 are now all implemented, tested, and merged.
2026-08-15 21:24:28 +02:00
|
|
|
3:
|
2026-08-15 14:43:46 +02:00
|
|
|
if elapsed < 0.3:
|
|
|
|
|
return false
|
|
|
|
|
var on_main: bool = current_scene != null and current_scene.name == "Main"
|
|
|
|
|
var reset_ok: bool = game_state.rocks == 500 and game_state.buildings.is_empty() \
|
|
|
|
|
and game_state.surviving_ship_count == GameState.BASE_FLEET_SIZE \
|
|
|
|
|
and game_state.planet_capture.is_empty()
|
|
|
|
|
print("on_main=%s reset_ok=%s rocks=%d buildings=%d ship_count=%d" % [
|
|
|
|
|
on_main, reset_ok, game_state.rocks, game_state.buildings.size(),
|
|
|
|
|
game_state.surviving_ship_count
|
|
|
|
|
])
|
|
|
|
|
if not on_main or not reset_ok:
|
|
|
|
|
return _fail("Play did not reset to a fresh game before reaching Main.tscn")
|
|
|
|
|
print("TEST_MAIN_MENU: PASS")
|
|
|
|
|
quit(0)
|
|
|
|
|
return true
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _press(button: IconButton) -> void:
|
|
|
|
|
var down: InputEventScreenTouch = InputEventScreenTouch.new()
|
|
|
|
|
down.pressed = true
|
|
|
|
|
button._gui_input(down)
|
|
|
|
|
var up: InputEventScreenTouch = InputEventScreenTouch.new()
|
|
|
|
|
up.pressed = false
|
|
|
|
|
button._gui_input(up)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
|
|
|
print("TEST_MAIN_MENU: FAIL (%s)" % message)
|
|
|
|
|
quit(1)
|
|
|
|
|
return true
|