A fleet of neon-outlined ships explores a procedurally generated, seeded infinite world: tap/joystick steering, pinch zoom, formation pivoting, planet landings, an ore economy with extractors and haulers, and player built structures (mothership, ship crafter, command centre). All art is hand-drawn with draw_* calls rather than textures, and the backgrounds are camera-relative shaders so they stay precise at large world coordinates. Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot tooling that renders real frames through Xvfb and Mesa softpipe, since --headless has no GPU. Excluded from version control: .godot/ (import cache, and it holds export credentials), builds/, android/build/ and packaged APKs - all regenerable, and ~325 MB combined. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
2.6 KiB
GDScript
93 lines
2.6 KiB
GDScript
extends Control
|
|
|
|
@export var min_zoom: float = 0.3
|
|
@export var max_zoom: float = 3.0
|
|
|
|
@onready var content: Node2D = $MapContent
|
|
@onready var open_button: IconButton = get_node("../MapButton")
|
|
@onready var recenter_button: IconButton = $RecenterButton
|
|
@onready var close_button: IconButton = $CloseButton
|
|
@onready var fleet: Node2D = get_node("../../Fleet")
|
|
|
|
var touches: Dictionary = {}
|
|
var pinch_active: bool = false
|
|
var pinch_start_distance: float = 0.0
|
|
var pinch_start_zoom: float = 1.0
|
|
|
|
|
|
func _ready() -> void:
|
|
open_button.pressed.connect(open_map)
|
|
recenter_button.pressed.connect(recenter_view)
|
|
close_button.pressed.connect(close_map)
|
|
|
|
|
|
func open_map() -> void:
|
|
visible = true
|
|
open_button.visible = false
|
|
recenter_view()
|
|
|
|
|
|
func close_map() -> void:
|
|
visible = false
|
|
open_button.visible = true
|
|
touches.clear()
|
|
pinch_active = false
|
|
|
|
|
|
func recenter_view() -> void:
|
|
var fleet_local: Vector2 = fleet.get_centroid() * content.WORLD_TO_LOCAL
|
|
var viewport_center: Vector2 = get_viewport_rect().size / 2.0
|
|
content.position = viewport_center - fleet_local * content.scale.x
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if not visible:
|
|
return
|
|
|
|
if event is InputEventScreenTouch:
|
|
_handle_touch(event)
|
|
get_viewport().set_input_as_handled()
|
|
elif event is InputEventScreenDrag:
|
|
_handle_drag(event)
|
|
get_viewport().set_input_as_handled()
|
|
elif event is InputEventMagnifyGesture:
|
|
_handle_magnify(event)
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
func _handle_touch(event: InputEventScreenTouch) -> void:
|
|
if event.pressed:
|
|
touches[event.index] = event.position
|
|
if touches.size() == 2:
|
|
pinch_active = true
|
|
var positions: Array = touches.values()
|
|
pinch_start_distance = positions[0].distance_to(positions[1])
|
|
pinch_start_zoom = content.scale.x
|
|
else:
|
|
touches.erase(event.index)
|
|
if touches.size() < 2:
|
|
pinch_active = false
|
|
|
|
|
|
func _handle_drag(event: InputEventScreenDrag) -> void:
|
|
if not touches.has(event.index):
|
|
return
|
|
var previous_pos: Vector2 = touches[event.index]
|
|
touches[event.index] = event.position
|
|
|
|
if pinch_active and touches.size() == 2 and pinch_start_distance > 0.0:
|
|
var positions: Array = touches.values()
|
|
var current_distance: float = positions[0].distance_to(positions[1])
|
|
var new_zoom: float = pinch_start_zoom * (current_distance / max(pinch_start_distance, 1.0))
|
|
_set_zoom(new_zoom)
|
|
elif touches.size() == 1:
|
|
content.position += event.position - previous_pos
|
|
|
|
|
|
func _handle_magnify(event: InputEventMagnifyGesture) -> void:
|
|
_set_zoom(content.scale.x * event.factor)
|
|
|
|
|
|
func _set_zoom(z: float) -> void:
|
|
var clamped: float = clamp(z, min_zoom, max_zoom)
|
|
content.scale = Vector2(clamped, clamped)
|