Reorganize project into autoload/, menu/, ships/, world/, assets/

Move flat top-level files into topic folders and rewrite every
res:// path reference (scenes, scripts, project.godot) accordingly.
Rename node_2d.tscn -> ships/ship.tscn and lowercase
images/ships/Example_ships -> assets/images/ships/example_ships
for naming consistency. Update overview/structure.md to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 20:28:15 -04:00
parent e1410c234a
commit 0c2628a1a6
51 changed files with 94 additions and 84 deletions
+246
View File
@@ -0,0 +1,246 @@
extends CanvasLayer
const RACES := [
{"id": 1, "name": "TERRAN FEDERATION", "sub": "Speed and balanced firepower", "color": Color(0.30, 0.55, 1.00)},
{"id": 2, "name": "THE COLLECTIVE", "sub": "Heavy armor, brute force", "color": Color(1.00, 0.45, 0.18)},
{"id": 3, "name": "IRON ORDER", "sub": "Precision strike force", "color": Color(0.28, 0.90, 0.42)},
]
const SHIPS := [
{
"name": "WARBIRD",
"role": "All-Round Fighter",
"desc": "The most versatile ship in the fleet. Solid speed, reliable firepower, and enough armor to stay in the fight. Forgiving to fly and effective in any situation — the go-to choice when you're not sure what you're up against.",
"path": "res://assets/images/ships/example_ships/1.png",
},
{
"name": "JAVELIN",
"role": "Fast Interceptor",
"desc": "Built for speed above everything else. Light armor means you have to pick your fights carefully, but nothing can outrun you. Best used for hit-and-run strikes, hunting weakened targets, and escaping when things go wrong.",
"path": "res://assets/images/ships/example_ships/1B.png",
},
{
"name": "SPIDER",
"role": "Heavy Support",
"desc": "Slow but extremely durable. High-volume fire makes it devastating in sustained engagements. Anchors a team by holding zones and suppressing enemy movements. Stay near allies — alone you're a target, together you're a wall.",
"path": "res://assets/images/ships/example_ships/2a.png",
},
{
"name": "LANCASTER",
"role": "Stealth Bomber",
"desc": "Medium speed with high burst damage potential. Built for patience — get into position, strike hard, and fade before the enemy can respond. Excels at flanking and ambushes. Requires discipline to play but punishes mistakes severely.",
"path": "res://assets/images/ships/example_ships/3b.png",
},
]
# Row layout constants (screen is 1280×800)
const ROW_X1 := 120.0
const ROW_X2 := 1160.0
const ROW_H := 130.0
const ROW_GAP := 10.0
const ROW_START := 195.0
const IMG_W := 130.0
var _root: Control
var _content: Control
var _offered: Array = []
var _chosen_race: Dictionary = {}
func _ready() -> void:
layer = 20
_offered = _pick_two_races()
_build_root()
_show_race_selection()
func _pick_two_races() -> Array:
var pool := RACES.duplicate()
pool.shuffle()
return [pool[0], pool[1]]
func _build_root() -> void:
_root = Control.new()
_root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
add_child(_root)
var overlay := ColorRect.new()
overlay.color = Color(0.0, 0.0, 0.0, 0.65)
overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
overlay.mouse_filter = Control.MOUSE_FILTER_STOP
_root.add_child(overlay)
func _swap_content() -> Control:
if _content:
_content.queue_free()
_content = Control.new()
_content.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_content.mouse_filter = Control.MOUSE_FILTER_IGNORE
_root.add_child(_content)
return _content
# ── Race selection ───────────────────────────────────────────────────────────
func _show_race_selection() -> void:
var c := _swap_content()
_label(c, "CHOOSE YOUR FACTION", 32, Color(0.42, 0.87, 1.0),
0, 105, 1280, 160, HORIZONTAL_ALIGNMENT_CENTER)
_label(c, "This match: %s vs %s" % [_offered[0].name, _offered[1].name],
13, Color(0.45, 0.58, 0.72),
0, 170, 1280, 200, HORIZONTAL_ALIGNMENT_CENTER)
var left_btn := _make_race_btn(_offered[0])
_place(left_btn, 150, 225, 590, 540)
left_btn.pressed.connect(_on_race_chosen.bind(_offered[0]))
c.add_child(left_btn)
var right_btn := _make_race_btn(_offered[1])
_place(right_btn, 690, 225, 1130, 540)
right_btn.pressed.connect(_on_race_chosen.bind(_offered[1]))
c.add_child(right_btn)
func _on_race_chosen(race: Dictionary) -> void:
_chosen_race = race
GameConfig.player_race = race.id
_show_ship_selection()
# ── Ship selection ───────────────────────────────────────────────────────────
func _show_ship_selection() -> void:
var c := _swap_content()
_label(c, "SELECT YOUR SHIP", 30, Color(0.42, 0.87, 1.0),
0, 90, 1280, 145, HORIZONTAL_ALIGNMENT_CENTER)
_label(c, "Playing as — " + _chosen_race.name, 14, _chosen_race.color,
0, 155, 1280, 185, HORIZONTAL_ALIGNMENT_CENTER)
for i in SHIPS.size():
var y := ROW_START + i * (ROW_H + ROW_GAP)
var btn := _make_ship_row(SHIPS[i], _chosen_race.color)
_place(btn, ROW_X1, y, ROW_X2, y + ROW_H)
btn.pressed.connect(_on_ship_chosen.bind(SHIPS[i]))
c.add_child(btn)
func _on_ship_chosen(ship: Dictionary) -> void:
GameConfig.player_ship_path = ship.path
GameConfig.team_selected.emit()
queue_free()
# ── Button builders ──────────────────────────────────────────────────────────
func _make_race_btn(race: Dictionary) -> Button:
var btn := Button.new()
btn.text = ""
var col: Color = race.color
var sn := _flat(Color(col.r * 0.14, col.g * 0.14, col.b * 0.14, 0.96), 12)
sn.set_border_width_all(2)
sn.border_color = Color(col.r * 0.38, col.g * 0.38, col.b * 0.38)
var sh := _flat(Color(col.r * 0.24, col.g * 0.24, col.b * 0.24, 0.96), 12)
sh.set_border_width_all(2)
sh.border_color = col
btn.add_theme_stylebox_override("normal", sn)
btn.add_theme_stylebox_override("hover", sh)
btn.add_theme_stylebox_override("pressed", sh)
var vb := VBoxContainer.new()
vb.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
vb.offset_left = 20; vb.offset_top = 24; vb.offset_right = -20; vb.offset_bottom = -24
vb.alignment = BoxContainer.ALIGNMENT_CENTER
vb.add_theme_constant_override("separation", 16)
vb.mouse_filter = Control.MOUSE_FILTER_IGNORE
btn.add_child(vb)
var name_lbl := Label.new()
name_lbl.text = race.name
name_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
name_lbl.add_theme_font_size_override("font_size", 28)
name_lbl.add_theme_color_override("font_color", col)
name_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
vb.add_child(name_lbl)
var sep := HSeparator.new()
sep.add_theme_color_override("color", Color(col.r * 0.4, col.g * 0.4, col.b * 0.4))
sep.mouse_filter = Control.MOUSE_FILTER_IGNORE
vb.add_child(sep)
var sub_lbl := Label.new()
sub_lbl.text = race.sub
sub_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
sub_lbl.add_theme_font_size_override("font_size", 15)
sub_lbl.add_theme_color_override("font_color", Color(0.60, 0.70, 0.82))
sub_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
vb.add_child(sub_lbl)
var hint := Label.new()
hint.text = "CLICK TO SELECT"
hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
hint.add_theme_font_size_override("font_size", 11)
hint.add_theme_color_override("font_color", Color(col.r * 0.5, col.g * 0.5, col.b * 0.5))
hint.mouse_filter = Control.MOUSE_FILTER_IGNORE
vb.add_child(hint)
return btn
func _make_ship_row(ship: Dictionary, accent: Color) -> Button:
var btn := Button.new()
btn.text = ""
var sn := _flat(Color(0.06, 0.09, 0.17, 0.95), 8)
sn.set_border_width_all(1)
sn.border_color = Color(0.18, 0.28, 0.48, 0.6)
var sh := _flat(Color(0.10, 0.16, 0.28, 0.97), 8)
sh.set_border_width_all(2)
sh.border_color = accent
btn.add_theme_stylebox_override("normal", sn)
btn.add_theme_stylebox_override("hover", sh)
btn.add_theme_stylebox_override("pressed", sh)
var tex := TextureRect.new()
tex.texture = load(ship.path)
tex.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
tex.offset_left = 8; tex.offset_top = 6; tex.offset_right = -8; tex.offset_bottom = -6
tex.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
tex.expand_mode = TextureRect.EXPAND_FIT_HEIGHT_PROPORTIONAL
tex.mouse_filter = Control.MOUSE_FILTER_IGNORE
btn.add_child(tex)
return btn
# ── Helpers ──────────────────────────────────────────────────────────────────
func _label(parent: Control, text: String, font_size: int, color: Color,
x1: float, y1: float, x2: float, y2: float,
align: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT) -> void:
var lbl := Label.new()
lbl.text = text
lbl.horizontal_alignment = align
lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
lbl.add_theme_font_size_override("font_size", font_size)
lbl.add_theme_color_override("font_color", color)
_place(lbl, x1, y1, x2, y2)
parent.add_child(lbl)
func _place(node: Control, x1: float, y1: float, x2: float, y2: float) -> void:
node.set_anchor_and_offset(SIDE_LEFT, 0.0, x1)
node.set_anchor_and_offset(SIDE_TOP, 0.0, y1)
node.set_anchor_and_offset(SIDE_RIGHT, 0.0, x2)
node.set_anchor_and_offset(SIDE_BOTTOM, 0.0, y2)
func _flat(col: Color, radius: int = 0) -> StyleBoxFlat:
var s := StyleBoxFlat.new()
s.bg_color = col
s.set_corner_radius_all(radius)
return s