The append-only rule was stated at the top of Icon and then restated eight more times down the list - "appended, same rule", "inserting anywhere earlier renumbers every icon int already stored in every scene" - about twenty-five lines carrying one fact. What each comment actually contributed was the glyph's subject: a wide hauler hull, a directional sensor dish, the workshop and the drone it sends out. Rule once at the top, then each entry says what it depicts. The DISTORDER note stays, reworded: the name is stale (it became the Energy Propulser) and CANNOT follow the art without renumbering, which is worth knowing. The enum order is byte-identical - 48 entries, verified against HEAD before committing. Reordering it would silently repoint every icon in every scene, which is the exact hazard the comment is about. This is the whole of what the sweep found. Searched for the same failure elsewhere with a near-duplicate detector and no other file repeats itself; the detector fires on the pre-fix version of this file, so its silence is worth something. The ticket's broader premise did not hold. Across 181 non-test files (24946 lines, 21% comments): the 79 single-line comments sitting above code state intent rather than action; GameState has no function comment that restates its own name and exactly two single-line in-body comments in 4219 lines; and the densest files are dense because the reasoning is - SurfaceBackground gives the arithmetic behind its bake resolution. Still open, and deliberately not done alone: 491 issue-number citations. Stripping them is a real simplification but they are traceable history, and the ticket warns against deleting what cannot be re-explained. 173/175 in the run, both misses being 120s timeouts under JOBS=4 contention; they pass in 81s and 79s run uncontended. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
987 lines
44 KiB
GDScript
987 lines
44 KiB
GDScript
@tool
|
|
class_name IconButton
|
|
extends Control
|
|
|
|
# Square neon-outlined button, hand-drawn to match the game's existing
|
|
# no-texture glowing look (same NeonDraw technique as ships/enemies) -
|
|
# no icon image assets, just a glyph drawn inside the frame.
|
|
|
|
signal pressed
|
|
|
|
# Scenes store `icon` as a plain int, so NEW ENTRIES MUST BE APPENDED.
|
|
# Inserting one anywhere but the end silently repoints every button after
|
|
# it, in every scene file that already names an icon by number. The entries
|
|
# below are therefore in the order they were added, not in any sensible one.
|
|
enum Icon {
|
|
MAP, CLOSE, SHIP, DOWN_ARROW, UP_ARROW, BUILD, ROTATE_CW, ROTATE_CCW,
|
|
# One per building, each a miniature of what actually gets placed. The
|
|
# build menu used to pick from the generic set, which left the mothership
|
|
# and the command centre sharing a folded-map glyph.
|
|
MOTHERSHIP, CRAFTER, COMMAND_CENTER, EXTRACTOR, TRANSPORT_HUB, SHIP_LAB,
|
|
# Two arrows passing each other. The single up/down arrows already mean
|
|
# "load" and "store" one way each, and this is where both live.
|
|
TRANSFER,
|
|
# Ship-lab upgrades, drawn as the EFFECT rather than the building that
|
|
# sells it, so a card full of them stays scannable.
|
|
UPGRADE_DAMAGE, UPGRADE_RATE, UPGRADE_RANGE, UPGRADE_THRUSTERS,
|
|
UPGRADE_SHIELD_HITS, UPGRADE_SHIELD_RECHARGE, UPGRADE_CARGO,
|
|
UPGRADE_ENGINES, UPGRADE_UNLOCK_SHIELD,
|
|
ACTION_MENU,
|
|
GAS,
|
|
LEFT_ARROW, RIGHT_ARROW,
|
|
MINUS, PLUS,
|
|
SHIELD_VESSEL,
|
|
# The ship and the thing it drops - the same two-icon split as
|
|
# LASER_VESSEL/LASER_SHOT below.
|
|
GAS_BOMBER, GAS_BOMB,
|
|
GAS_BURNER,
|
|
# A wide hauler hull, distinct from every vessel glyph above.
|
|
CARRIER_VESSEL,
|
|
LASER_VESSEL, LASER_SHOT,
|
|
# Drawn as an engine bell, not the warped ring the name suggests: this
|
|
# was a "space-time distorder" before it became the Energy Propulser.
|
|
# The name cannot follow the art without renumbering everything after it.
|
|
DISTORDER_VESSEL,
|
|
# Switching a converter off and on.
|
|
PAUSE, RESUME,
|
|
OUTPOST,
|
|
PLANNING_CENTER,
|
|
# The workshop and the drone it sends out.
|
|
WORKSHOP, REPAIR_DRONE,
|
|
# The standing objective.
|
|
QUEST,
|
|
# A fleet-wide thrust burst, distinct from the Propulser vessel icon.
|
|
DASH,
|
|
# Three sliders. Used only by the editor-only sandbox button.
|
|
DEV_TOOLS,
|
|
# A directional sensor dish.
|
|
SCANNER,
|
|
}
|
|
|
|
# Size the glyphs were originally drawn against. Every glyph dimension is
|
|
# scaled by (actual size / this), so the button can be resized in the scene
|
|
# without the icon inside it staying small.
|
|
const REFERENCE_SIZE: float = 56.0
|
|
|
|
@export var icon: int = Icon.MAP
|
|
@export var glow_color: Color = Color(0.25, 0.85, 1.0)
|
|
# Selection is an additional frame, never a recolour. It lets menus retain
|
|
# semantic colours (vessel class, danger, cargo) while still making the
|
|
# active choice unmistakable.
|
|
# #112: the glyph without the button around it. The skill tree draws its own
|
|
# hexagon for each node and wants only what is inside one, and a second frame
|
|
# nested in that hexagon reads as a button sitting on a node rather than as
|
|
# the node's own mark.
|
|
@export var show_frame: bool = true
|
|
@export var highlighted: bool = false: set = set_highlighted
|
|
# Colour of the bright core stroke. Defaults to near-white, which reads well
|
|
# on the dark space background; scenes drawing over a bright backdrop (the
|
|
# lunar surface) override this with a dark colour so the button doesn't wash
|
|
# out. See NeonDraw.
|
|
@export var core_color: Color = Color(1.0, 1.0, 1.0)
|
|
# When false the button is drawn greyed out, but STILL emits `pressed` -
|
|
# the caller decides what to do, which is what lets an unavailable option
|
|
# explain itself instead of silently doing nothing.
|
|
@export var enabled: bool = true: set = set_enabled
|
|
@export var disabled_color: Color = Color(0.42, 0.45, 0.5)
|
|
|
|
var pressed_down: bool = false
|
|
|
|
|
|
func set_enabled(value: bool) -> void:
|
|
if enabled == value:
|
|
return
|
|
enabled = value
|
|
queue_redraw()
|
|
|
|
|
|
func set_highlighted(value: bool) -> void:
|
|
if highlighted == value:
|
|
return
|
|
highlighted = value
|
|
queue_redraw()
|
|
|
|
|
|
func _ready() -> void:
|
|
queue_redraw()
|
|
|
|
|
|
func _blocked_by_modal() -> bool:
|
|
# A panel is open and this button is not part of it, so it is sitting
|
|
# BEHIND the panel and must not react.
|
|
#
|
|
# The panels cannot simply swallow GUI input with a mouse_filter of STOP:
|
|
# the skill tree and the formation editor are driven from raw
|
|
# _unhandled_input (see SkillTreeView), and a Control that consumes
|
|
# touches through the GUI system swallows them before they ever get
|
|
# there. So the panels stay transparent to input and the buttons check
|
|
# instead.
|
|
var own_modal: Node = _own_modal()
|
|
for menu in get_tree().get_nodes_in_group("modal_menus"):
|
|
# is_visible_in_tree(), NOT visible. Hiding a parent leaves the
|
|
# child's own `visible` flag true, and the build menu's info popup is
|
|
# itself a modal panel - so closing the build menu with the X left an
|
|
# invisible popup still claiming to be open, and every button in the
|
|
# game was blocked from then on. That is not a state a player can
|
|
# get out of.
|
|
if not menu.is_visible_in_tree():
|
|
continue
|
|
if menu.is_ancestor_of(self):
|
|
continue
|
|
# Stacking order matters, and used not to be considered at all. A
|
|
# panel that this button's own panel is drawn ON TOP OF sits behind
|
|
# it, not over it, so it must not swallow the input. The research
|
|
# shortfall popup opens over the skill tree and the ship lab, and
|
|
# without this every button in it - its close button included - was
|
|
# dead, leaving the player no way out of the dialog.
|
|
if own_modal != null and not _covers(menu, own_modal):
|
|
continue
|
|
return true
|
|
return false
|
|
|
|
|
|
# The modal panel this button lives inside, or null for a HUD button that
|
|
# belongs to no panel (those stay blocked by any open panel, as before).
|
|
func _own_modal() -> Node:
|
|
var node: Node = get_parent()
|
|
while node != null:
|
|
if node.is_in_group("modal_menus"):
|
|
return node
|
|
node = node.get_parent()
|
|
return null
|
|
|
|
|
|
func _covers(other: Node, own: Node) -> bool:
|
|
# Later siblings draw on top. Different parents means one of them is
|
|
# nested inside something else - the build menu's info popup is a child
|
|
# of the build menu - and there the conservative answer is "covered".
|
|
if other.get_parent() != own.get_parent():
|
|
return true
|
|
return other.get_index() > own.get_index()
|
|
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
|
# @tool runs this in the editor too, where a click on the canvas would
|
|
# emit `pressed` at whatever it is wired to. The glyph is what we want
|
|
# visible there, not the behaviour.
|
|
if Engine.is_editor_hint():
|
|
return
|
|
if _blocked_by_modal():
|
|
return
|
|
if event is InputEventScreenTouch:
|
|
_handle_press(event.pressed, event.position)
|
|
accept_event()
|
|
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
|
_handle_press(event.pressed, event.position)
|
|
accept_event()
|
|
|
|
|
|
# Complete the press immediately instead of waiting for a release.
|
|
#
|
|
# Measured on the device, in the web build: an on-screen probe reported
|
|
# `touch=1 mouse=1 held=y` after one tap - one touch down, one emulated mouse
|
|
# down, the button latched into its held state through ordinary GUI routing,
|
|
# and then nothing. The release is never delivered. A button that only emits
|
|
# on release therefore never emits at all.
|
|
#
|
|
# GUI routing itself works fine; `held=y` is the proof. An earlier attempt to
|
|
# bypass it made things worse and hid the evidence.
|
|
#
|
|
# The cost is the drag-off-to-cancel affordance below. Everywhere that
|
|
# delivers releases keeps it.
|
|
@export var fire_on_press: bool = OS.has_feature("web")
|
|
|
|
# Round rather than square. The in-world action controls - the abilities
|
|
# arranged around the fire stick, and the pivot pair - read as a different
|
|
# kind of thing from the square panel and menu buttons, and a round target is
|
|
# what a thumb reaching for an ability expects. Purely how the frame is drawn;
|
|
# the hit area is still the whole rect.
|
|
@export var round_frame: bool = false
|
|
|
|
|
|
# The frame outline, inset by `inset` pixels on every side. Round buttons trace
|
|
# a circle through the same inset box, so a round and a square button of the
|
|
# same size present the same footprint.
|
|
func _frame_points(inset: float) -> PackedVector2Array:
|
|
if not round_frame:
|
|
return PackedVector2Array([
|
|
Vector2(inset, inset),
|
|
Vector2(size.x - inset, inset),
|
|
Vector2(size.x - inset, size.y - inset),
|
|
Vector2(inset, size.y - inset),
|
|
])
|
|
var centre: Vector2 = size / 2.0
|
|
var radius: Vector2 = centre - Vector2(inset, inset)
|
|
var points: PackedVector2Array = PackedVector2Array()
|
|
for i in range(32):
|
|
var angle: float = TAU * float(i) / 32.0
|
|
points.append(centre + Vector2(cos(angle) * radius.x, sin(angle) * radius.y))
|
|
return points
|
|
|
|
|
|
func _handle_press(is_pressed: bool, at_position: Vector2) -> void:
|
|
if is_pressed:
|
|
pressed_down = true
|
|
queue_redraw()
|
|
if fire_on_press:
|
|
# Not latched: nothing is coming to clear it.
|
|
pressed_down = false
|
|
pressed.emit()
|
|
elif pressed_down:
|
|
pressed_down = false
|
|
queue_redraw()
|
|
# Only counts as a real press if the finger/cursor is still over
|
|
# the button when it lifts - dragging off before releasing (a
|
|
# common way to "cancel" a touch) must not still trigger it.
|
|
if Rect2(Vector2.ZERO, size).has_point(at_position):
|
|
pressed.emit()
|
|
|
|
|
|
func _active_color() -> Color:
|
|
return glow_color if enabled else disabled_color
|
|
|
|
|
|
func _draw() -> void:
|
|
var rect_size: Vector2 = size
|
|
var s: float = min(rect_size.x, rect_size.y) / REFERENCE_SIZE
|
|
# Constant glow, brighter while held. Only the press state changes it,
|
|
# so the button redraws on press/release instead of every frame.
|
|
var pulse: float = 1.0 if pressed_down else 0.85
|
|
|
|
var margin: float = 4.0 * s
|
|
if show_frame:
|
|
NeonDraw.draw_glowing_polygon(self, _frame_points(margin), _active_color(),
|
|
pulse, core_color)
|
|
if highlighted and show_frame:
|
|
# The inset frame reads as a selection focus ring. It deliberately uses
|
|
# the bright core rather than the button's glow colour, so selecting a
|
|
# blue shield or pink transporter does not change what that colour means.
|
|
NeonDraw.draw_glowing_polygon(self, _frame_points(10.0 * s), core_color, 1.0,
|
|
core_color, 0.72 * s)
|
|
|
|
var center: Vector2 = rect_size / 2.0
|
|
match icon:
|
|
Icon.CLOSE:
|
|
_draw_close(center, pulse, s)
|
|
Icon.SHIP:
|
|
_draw_ship(center, pulse, s)
|
|
Icon.MAP:
|
|
_draw_map(center, pulse, s)
|
|
Icon.DOWN_ARROW:
|
|
_draw_arrow(center, pulse, 1.0, s)
|
|
Icon.UP_ARROW:
|
|
_draw_arrow(center, pulse, -1.0, s)
|
|
Icon.BUILD:
|
|
_draw_build(center, pulse, s)
|
|
Icon.ROTATE_CW:
|
|
_draw_rotate(center, pulse, s, 1.0)
|
|
Icon.ROTATE_CCW:
|
|
_draw_rotate(center, pulse, s, -1.0)
|
|
Icon.MOTHERSHIP:
|
|
_draw_mothership_icon(center, pulse, s)
|
|
Icon.CRAFTER:
|
|
_draw_crafter_icon(center, pulse, s)
|
|
Icon.COMMAND_CENTER:
|
|
_draw_command_icon(center, pulse, s)
|
|
Icon.EXTRACTOR:
|
|
_draw_extractor_icon(center, pulse, s)
|
|
Icon.TRANSPORT_HUB:
|
|
_draw_transport_hub_icon(center, pulse, s)
|
|
Icon.SHIP_LAB:
|
|
_draw_ship_lab_icon(center, pulse, s)
|
|
Icon.TRANSFER:
|
|
_draw_transfer_icon(center, pulse, s)
|
|
Icon.UPGRADE_DAMAGE:
|
|
_draw_upgrade_damage(center, pulse, s)
|
|
Icon.UPGRADE_RATE:
|
|
_draw_upgrade_rate(center, pulse, s)
|
|
Icon.UPGRADE_RANGE:
|
|
_draw_upgrade_range(center, pulse, s)
|
|
Icon.UPGRADE_THRUSTERS:
|
|
_draw_upgrade_thrusters(center, pulse, s)
|
|
Icon.UPGRADE_SHIELD_HITS:
|
|
_draw_upgrade_shield_hits(center, pulse, s)
|
|
Icon.UPGRADE_SHIELD_RECHARGE:
|
|
_draw_upgrade_shield_recharge(center, pulse, s)
|
|
Icon.UPGRADE_CARGO:
|
|
_draw_upgrade_cargo(center, pulse, s)
|
|
Icon.UPGRADE_ENGINES:
|
|
_draw_upgrade_engines(center, pulse, s)
|
|
Icon.UPGRADE_UNLOCK_SHIELD:
|
|
_draw_upgrade_unlock_shield(center, pulse, s)
|
|
Icon.ACTION_MENU:
|
|
_draw_action_menu(center, pulse, s)
|
|
Icon.GAS:
|
|
_draw_gas(center, pulse, s)
|
|
Icon.LEFT_ARROW:
|
|
_draw_horizontal_arrow(center, pulse, -1.0, s)
|
|
Icon.RIGHT_ARROW:
|
|
_draw_horizontal_arrow(center, pulse, 1.0, s)
|
|
Icon.MINUS:
|
|
_draw_minus(center, pulse, s)
|
|
Icon.PLUS:
|
|
_draw_plus(center, pulse, s)
|
|
Icon.SHIELD_VESSEL:
|
|
_draw_shield_vessel(center, pulse, s)
|
|
Icon.GAS_BOMBER:
|
|
_draw_gas_bomber(center, pulse, s)
|
|
Icon.GAS_BOMB:
|
|
_draw_gas_bomb(center, pulse, s)
|
|
Icon.GAS_BURNER:
|
|
_draw_gas_burner_icon(center, pulse, s)
|
|
Icon.PAUSE:
|
|
_draw_pause(center, pulse, s)
|
|
Icon.RESUME:
|
|
_draw_resume(center, pulse, s)
|
|
Icon.CARRIER_VESSEL:
|
|
_draw_carrier(center, pulse, s)
|
|
Icon.LASER_VESSEL:
|
|
_draw_laser_vessel(center, pulse, s)
|
|
Icon.LASER_SHOT:
|
|
_draw_laser_shot(center, pulse, s)
|
|
Icon.DISTORDER_VESSEL:
|
|
_draw_distorder_vessel(center, pulse, s)
|
|
Icon.OUTPOST:
|
|
_draw_outpost_icon(center, pulse, s)
|
|
Icon.PLANNING_CENTER:
|
|
_draw_planning_icon(center, pulse, s)
|
|
Icon.WORKSHOP:
|
|
_draw_workshop_icon(center, pulse, s)
|
|
Icon.REPAIR_DRONE:
|
|
_draw_repair_drone_icon(center, pulse, s)
|
|
Icon.QUEST:
|
|
_draw_quest_icon(center, pulse, s)
|
|
Icon.DASH:
|
|
_draw_dash_icon(center, pulse, s)
|
|
Icon.DEV_TOOLS:
|
|
_draw_dev_tools_icon(center, pulse, s)
|
|
Icon.SCANNER:
|
|
_draw_scanner_icon(center, pulse, s)
|
|
|
|
|
|
func _draw_scanner_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# Dish, mast and three outward wavefronts: distinct from MAP (a folded
|
|
# sheet) and RESEARCH (the mothership's archive).
|
|
NeonDraw.draw_glowing_path(self, PackedVector2Array([
|
|
center + Vector2(-12, -3) * s,
|
|
center + Vector2(-5, 7) * s,
|
|
center + Vector2(5, 4) * s,
|
|
]), _active_color(), pulse, 0.6 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-4, 7) * s,
|
|
center + Vector2(-4, 14) * s, _active_color(), pulse, 0.6 * s,
|
|
core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-11, 14) * s,
|
|
center + Vector2(3, 14) * s, _active_color(), pulse, 0.6 * s,
|
|
core_color)
|
|
for radius in [7.0, 13.0, 19.0]:
|
|
draw_arc(center + Vector2(-7, 1) * s, radius * s, -1.1, -0.15, 10,
|
|
_active_color(), maxf(1.0, 1.3 * s), true)
|
|
|
|
|
|
func _draw_dev_tools_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A compact bank of adjustment sliders. It reads as configuration rather
|
|
# than as the old BUILD glyph, which made the wave shortcut look like a
|
|
# second construction button.
|
|
for row in range(3):
|
|
var y: float = (-9.0 + float(row) * 9.0) * s
|
|
var left: Vector2 = center + Vector2(-13.0 * s, y)
|
|
var right: Vector2 = center + Vector2(13.0 * s, y)
|
|
NeonDraw.draw_glowing_line(self, left, right, _active_color(), pulse,
|
|
0.45 * s, core_color)
|
|
var knob_x: float = [-5.0, 7.0, -1.0][row] * s
|
|
NeonDraw.draw_glowing_circle(self, center + Vector2(knob_x, y),
|
|
2.7 * s, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_dash_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
var tip: Vector2 = center + Vector2(12.0, 0.0) * s
|
|
var tail: Vector2 = center + Vector2(-5.0, 0.0) * s
|
|
NeonDraw.draw_glowing_line(self, tail, tip, _active_color(), pulse,
|
|
0.7 * s, core_color)
|
|
NeonDraw.draw_glowing_path(self, PackedVector2Array([
|
|
tip + Vector2(-7.0, -7.0) * s, tip,
|
|
tip + Vector2(-7.0, 7.0) * s,
|
|
]), _active_color(), pulse, 0.7 * s, core_color)
|
|
for y in [-7.0, 0.0, 7.0]:
|
|
NeonDraw.draw_glowing_line(self,
|
|
center + Vector2(-13.0, y) * s,
|
|
center + Vector2(-7.0, y) * s,
|
|
_active_color(), pulse * 0.7, 0.45 * s, core_color)
|
|
|
|
|
|
# #140: a checklist - a page with a tick against the top line. Reads as
|
|
# "what I have been asked to do" rather than as a map, a target or a flag,
|
|
# each of which this button sits next to.
|
|
func _draw_quest_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
var half: Vector2 = Vector2(9.0, 12.0) * s
|
|
NeonDraw.draw_glowing_path(self, PackedVector2Array([
|
|
center + Vector2(-half.x, -half.y), center + Vector2(half.x, -half.y),
|
|
center + Vector2(half.x, half.y), center + Vector2(-half.x, half.y),
|
|
center + Vector2(-half.x, -half.y),
|
|
]), _active_color(), pulse, 0.5 * s, core_color)
|
|
# The done line, ticked; the two below it still open.
|
|
var tick: Vector2 = center + Vector2(-5.0, -5.0) * s
|
|
NeonDraw.draw_glowing_path(self, PackedVector2Array([
|
|
tick + Vector2(-1.5, 0.0) * s, tick + Vector2(0.5, 2.0) * s,
|
|
tick + Vector2(3.5, -2.5) * s,
|
|
]), _active_color(), pulse, 0.5 * s, core_color)
|
|
for row in [1.0, 5.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-5.5, row) * s,
|
|
center + Vector2(5.5, row) * s, _active_color(), pulse * 0.75,
|
|
0.4 * s, core_color)
|
|
|
|
|
|
func _draw_close(center: Vector2, pulse: float, s: float) -> void:
|
|
var r: float = 10.0 * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-r, -r), center + Vector2(r, r), _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-r, r), center + Vector2(r, -r), _active_color(), pulse, 0.5 * s, core_color)
|
|
|
|
|
|
func _draw_ship(center: Vector2, pulse: float, s: float) -> void:
|
|
var nose: Vector2 = center + Vector2(0, -11) * s
|
|
var left: Vector2 = center + Vector2(-7, 8) * s
|
|
var right: Vector2 = center + Vector2(7, 8) * s
|
|
var points: PackedVector2Array = PackedVector2Array([nose, left, right])
|
|
NeonDraw.draw_glowing_polygon(self, points, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_shield_vessel(center: Vector2, pulse: float, s: float) -> void:
|
|
var points: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var angle: float = -PI / 2.0 + float(i) * TAU / 6.0
|
|
points.append(center + Vector2(cos(angle), sin(angle)) * 11.0 * s)
|
|
NeonDraw.draw_glowing_polygon(self, points, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_carrier(center: Vector2, pulse: float, s: float) -> void:
|
|
# Wide and flat-bottomed rather than pointed - reads as a hauler, not a
|
|
# combat silhouette, matching the ship's own hull_points.
|
|
var hull := PackedVector2Array([
|
|
center + Vector2(-13, -4) * s,
|
|
center + Vector2(-7, -8) * s,
|
|
center + Vector2(7, -8) * s,
|
|
center + Vector2(13, -4) * s,
|
|
center + Vector2(13, 6) * s,
|
|
center + Vector2(-13, 6) * s,
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, hull, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_distorder_vessel(center: Vector2, pulse: float, s: float) -> void:
|
|
# #77: an engine bell with a thrust plume under it. The glyph used to be a
|
|
# ring with a wobbling radius, which read as warped space - correct for a
|
|
# "space-time distorder" and wrong for the Energy Propulser it now is.
|
|
var hull: PackedVector2Array = PackedVector2Array([
|
|
center + Vector2(0.0, -13.0) * s,
|
|
center + Vector2(5.0, -4.0) * s,
|
|
center + Vector2(7.0, 3.0) * s,
|
|
center + Vector2(10.0, 10.0) * s,
|
|
center + Vector2(-10.0, 10.0) * s,
|
|
center + Vector2(-7.0, 3.0) * s,
|
|
center + Vector2(-5.0, -4.0) * s,
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, hull, _active_color(), pulse, core_color)
|
|
# Three exhaust strokes, the middle one longest, so the thrust reads as
|
|
# coming OUT of the bell rather than as decoration beside it.
|
|
for offset in [-5.0, 0.0, 5.0]:
|
|
var length: float = 16.0 if is_zero_approx(offset) else 12.0
|
|
NeonDraw.draw_glowing_line(self,
|
|
center + Vector2(offset, 11.0) * s,
|
|
center + Vector2(offset, length) * s,
|
|
_active_color(), pulse * 0.8, 0.5, core_color)
|
|
|
|
|
|
func _draw_laser_vessel(center: Vector2, pulse: float, s: float) -> void:
|
|
# A slim dart rather than the interceptor's plain triangle - the notch at
|
|
# the nose reads as a fixed forward-facing emitter, not a hull point.
|
|
var hull := PackedVector2Array([
|
|
center + Vector2(0, -13) * s,
|
|
center + Vector2(-8, 9) * s,
|
|
center + Vector2(-3, 6) * s,
|
|
center + Vector2(3, 6) * s,
|
|
center + Vector2(8, 9) * s,
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, hull, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_laser_shot(center: Vector2, pulse: float, s: float) -> void:
|
|
var from: Vector2 = center + Vector2(-9, 9) * s
|
|
var to: Vector2 = center + Vector2(9, -9) * s
|
|
NeonDraw.draw_glowing_line(self, from, to, _active_color(), pulse, 1.1, core_color)
|
|
NeonDraw.draw_glowing_circle(self, to, 3.0 * s, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_gas_bomber(center: Vector2, pulse: float, s: float) -> void:
|
|
var hull := PackedVector2Array([
|
|
center + Vector2(0, -12) * s,
|
|
center + Vector2(-12, 7) * s,
|
|
center + Vector2(-5, 5) * s,
|
|
center + Vector2(0, 11) * s,
|
|
center + Vector2(5, 5) * s,
|
|
center + Vector2(12, 7) * s,
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, hull, _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_circle(self, center + Vector2(0, 2) * s, 4.0 * s,
|
|
_active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_gas_bomb(center: Vector2, pulse: float, s: float) -> void:
|
|
NeonDraw.draw_glowing_circle(self, center, 7.0 * s, _active_color(), pulse, core_color)
|
|
for direction in [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT]:
|
|
NeonDraw.draw_glowing_circle(self, center + direction * 9.0 * s, 3.5 * s,
|
|
_active_color(), pulse * 0.85, core_color)
|
|
|
|
|
|
func _draw_gas_burner_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A hexagonal chamber with a hot core and vents, miniaturised from the
|
|
# building's own silhouette (BuildingVisualBase._draw_gas_burner_static)
|
|
# so the menu icon reads as the same structure being placed.
|
|
var outer: PackedVector2Array = PackedVector2Array()
|
|
var core: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
outer.append(center + Vector2(cos(a), sin(a)) * 11.0 * s)
|
|
core.append(center + Vector2(cos(a), sin(a)) * 5.5 * s)
|
|
NeonDraw.draw_glowing_polygon(self, outer, _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_polygon(self, core, _active_color(), pulse * 0.85, core_color)
|
|
|
|
for i in range(4):
|
|
var a: float = (float(i) / 4.0) * TAU + PI / 4.0
|
|
var dir: Vector2 = Vector2(cos(a), sin(a))
|
|
NeonDraw.draw_glowing_line(self, center + dir * 11.0 * s, center + dir * 15.5 * s,
|
|
_active_color(), pulse, 0.35 * s, core_color)
|
|
|
|
|
|
func _draw_arrow(center: Vector2, pulse: float, direction: float, s: float) -> void:
|
|
var r: float = 9.0 * s
|
|
var tip: Vector2 = center + Vector2(0, r * direction)
|
|
var shaft_top: Vector2 = center + Vector2(0, -r * direction)
|
|
NeonDraw.draw_glowing_line(self, shaft_top, tip, _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(-r * 0.7, -r * 0.8 * direction), _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(r * 0.7, -r * 0.8 * direction), _active_color(), pulse, 0.5 * s, core_color)
|
|
|
|
|
|
func _draw_horizontal_arrow(center: Vector2, pulse: float, direction: float, s: float) -> void:
|
|
var r: float = 10.0 * s
|
|
var tip: Vector2 = center + Vector2(r * direction, 0.0)
|
|
var tail: Vector2 = center + Vector2(-r * direction, 0.0)
|
|
NeonDraw.draw_glowing_line(self, tail, tip, _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(-r * 0.8 * direction, -r * 0.7), _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(-r * 0.8 * direction, r * 0.7), _active_color(), pulse, 0.5 * s, core_color)
|
|
|
|
|
|
func _draw_pause(center: Vector2, pulse: float, s: float) -> void:
|
|
# Two upright bars. Shown while something is RUNNING, because that is the
|
|
# action the button performs, not the state it is in.
|
|
for side in [-1.0, 1.0]:
|
|
var x: float = 5.0 * side * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(x, -11.0 * s),
|
|
center + Vector2(x, 11.0 * s), _active_color(), pulse, 0.9, core_color)
|
|
|
|
|
|
func _draw_resume(center: Vector2, pulse: float, s: float) -> void:
|
|
# A play triangle, shown while something is stopped.
|
|
var play: PackedVector2Array = PackedVector2Array([
|
|
center + Vector2(-7.0, -11.0) * s,
|
|
center + Vector2(11.0, 0.0) * s,
|
|
center + Vector2(-7.0, 11.0) * s,
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, play, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_minus(center: Vector2, pulse: float, s: float) -> void:
|
|
var r: float = 9.0 * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-r, 0.0), center + Vector2(r, 0.0),
|
|
_active_color(), pulse, 0.55 * s, core_color)
|
|
|
|
|
|
func _draw_plus(center: Vector2, pulse: float, s: float) -> void:
|
|
_draw_minus(center, pulse, s)
|
|
var r: float = 9.0 * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0.0, -r), center + Vector2(0.0, r),
|
|
_active_color(), pulse, 0.55 * s, core_color)
|
|
|
|
|
|
func _draw_transfer_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# Two shafts side by side pointing opposite ways. Drawn as a pair rather
|
|
# than as one double-headed arrow because the button's whole job is that
|
|
# ore moves BOTH ways here, and a double-headed arrow reads as one thing
|
|
# sliding back and forth.
|
|
var r: float = 10.0 * s
|
|
var gap: float = 7.0 * s
|
|
for side in [-1.0, 1.0]:
|
|
# Left shaft points up (into the fleet), right shaft points down.
|
|
var direction: float = side
|
|
var x: float = gap * side
|
|
var tip: Vector2 = center + Vector2(x, r * direction)
|
|
var tail: Vector2 = center + Vector2(x, -r * direction)
|
|
NeonDraw.draw_glowing_line(self, tail, tip, _active_color(), pulse, 0.42 * s, core_color)
|
|
var head: float = 5.0 * s
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(-head, -head * direction),
|
|
_active_color(), pulse, 0.42 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(head, -head * direction),
|
|
_active_color(), pulse, 0.42 * s, core_color)
|
|
|
|
|
|
# --- ship-lab upgrade glyphs ---------------------------------------------
|
|
|
|
func _draw_upgrade_damage(center: Vector2, pulse: float, s: float) -> void:
|
|
# A heavy projectile striking a target.
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-13, 0) * s, center + Vector2(5, 0) * s, _active_color(), pulse, 0.7 * s, core_color)
|
|
for a in [0.0, PI * 0.5, PI, PI * 1.5]:
|
|
var dir := Vector2(cos(a), sin(a))
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(8, 0) * s, center + Vector2(8, 0) * s + dir * 7.0 * s, _active_color(), pulse, 0.4 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_rate(center: Vector2, pulse: float, s: float) -> void:
|
|
# Three successive shots: more fire per second.
|
|
for x in [-9.0, 0.0, 9.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(x - 3, 7) * s, center + Vector2(x + 3, -7) * s, _active_color(), pulse, 0.55 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_range(center: Vector2, pulse: float, s: float) -> void:
|
|
# Reticle with a long sight line.
|
|
var r := 10.0 * s
|
|
for i in range(12):
|
|
var a := TAU * float(i) / 12.0
|
|
var b := TAU * float(i + 1) / 12.0
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(cos(a), sin(a)) * r, center + Vector2(cos(b), sin(b)) * r, _active_color(), pulse, 0.32 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-15, 0) * s, center + Vector2(15, 0) * s, _active_color(), pulse, 0.38 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0, -15) * s, center + Vector2(0, 15) * s, _active_color(), pulse, 0.38 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_thrusters(center: Vector2, pulse: float, s: float) -> void:
|
|
# Ship nozzle with two exhaust flames.
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([center + Vector2(-10, -7) * s, center + Vector2(5, -7) * s, center + Vector2(11, 0) * s, center + Vector2(5, 7) * s, center + Vector2(-10, 7) * s]), _active_color(), pulse, core_color)
|
|
for y in [-4.0, 4.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-10, y) * s, center + Vector2(-17, y * 1.5) * s, _active_color(), pulse, 0.45 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_shield_hits(center: Vector2, pulse: float, s: float) -> void:
|
|
# Layered hexagonal shield walls.
|
|
for r in [8.0, 13.0]:
|
|
var hex := PackedVector2Array()
|
|
for i in range(6):
|
|
var a := TAU * float(i) / 6.0 + PI / 6.0
|
|
hex.append(center + Vector2(cos(a), sin(a)) * r * s)
|
|
NeonDraw.draw_glowing_polygon(self, hex, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_upgrade_shield_recharge(center: Vector2, pulse: float, s: float) -> void:
|
|
# A shield with a circular recovery arrow.
|
|
var arc := PackedVector2Array()
|
|
for i in range(10):
|
|
var a := lerpf(-PI * 0.2, PI * 1.35, float(i) / 9.0)
|
|
arc.append(center + Vector2(cos(a), sin(a)) * 11.0 * s)
|
|
NeonDraw.draw_glowing_path(self, arc, _active_color(), pulse, 0.45 * s, core_color)
|
|
var tip: Vector2 = arc[arc.size() - 1]
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(6, -1) * s, _active_color(), pulse, 0.45 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip + Vector2(1, 6) * s, _active_color(), pulse, 0.45 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_cargo(center: Vector2, pulse: float, s: float) -> void:
|
|
# Freight crate, with outward arrows suggesting expanded volume.
|
|
var box := 8.0 * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([center + Vector2(-box, -box) , center + Vector2(box, -box), center + Vector2(box, box), center + Vector2(-box, box)]), _active_color(), pulse, core_color)
|
|
for side in [-1.0, 1.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(side * 10, 0) * s, center + Vector2(side * 16, 0) * s, _active_color(), pulse, 0.38 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_engines(center: Vector2, pulse: float, s: float) -> void:
|
|
# Engine core with speed streaks.
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([center + Vector2(-4, -8) * s, center + Vector2(7, -5) * s, center + Vector2(10, 0) * s, center + Vector2(7, 5) * s, center + Vector2(-4, 8) * s, center + Vector2(-8, 0) * s]), _active_color(), pulse, core_color)
|
|
for y in [-7.0, 0.0, 7.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-18, y) * s, center + Vector2(-9, y) * s, _active_color(), pulse, 0.4 * s, core_color)
|
|
|
|
|
|
func _draw_upgrade_unlock_shield(center: Vector2, pulse: float, s: float) -> void:
|
|
# A shield silhouette with a plus: unlock the whole vessel, not a stat.
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([center + Vector2(0, -13) * s, center + Vector2(11, -7) * s, center + Vector2(8, 7) * s, center + Vector2(0, 13) * s, center + Vector2(-8, 7) * s, center + Vector2(-11, -7) * s]), _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-4, 0) * s, center + Vector2(4, 0) * s, _active_color(), pulse, 0.45 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0, -4) * s, center + Vector2(0, 4) * s, _active_color(), pulse, 0.45 * s, core_color)
|
|
|
|
|
|
func _draw_action_menu(center: Vector2, pulse: float, s: float) -> void:
|
|
# A small, intentionally neutral stack of choices. It marks a button that
|
|
# opens context actions rather than carrying out one particular action.
|
|
for y in [-8.0, 0.0, 8.0]:
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-10, y) * s, center + Vector2(10, y) * s, _active_color(), pulse, 0.42 * s, core_color)
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(-15, y - 2) * s, center + Vector2(-11, y) * s,
|
|
center + Vector2(-15, y + 2) * s,
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
func _draw_gas(center: Vector2, pulse: float, s: float) -> void:
|
|
# Three drifting bubbles: the visual language for gaseous resources.
|
|
for data in [[Vector2(-6,-3), 6.0], [Vector2(6,4), 8.0], [Vector2(4,-9), 4.0]]:
|
|
var ring := PackedVector2Array()
|
|
for i in range(10):
|
|
var a := TAU * float(i) / 10.0
|
|
ring.append(center + (data[0] + Vector2(cos(a), sin(a)) * data[1]) * s)
|
|
NeonDraw.draw_glowing_polygon(self, ring, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_build(center: Vector2, pulse: float, s: float) -> void:
|
|
# A plus inside a frame - "add a structure".
|
|
var r: float = 11.0 * s
|
|
var frame: PackedVector2Array = PackedVector2Array([
|
|
center + Vector2(-r, -r), center + Vector2(r, -r),
|
|
center + Vector2(r, r), center + Vector2(-r, r),
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, frame, _active_color(), pulse, core_color)
|
|
var arm: float = r * 0.55
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-arm, 0), center + Vector2(arm, 0), _active_color(), pulse, 0.4 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0, -arm), center + Vector2(0, arm), _active_color(), pulse, 0.4 * s, core_color)
|
|
|
|
|
|
func _draw_rotate(center: Vector2, pulse: float, s: float, direction: float) -> void:
|
|
# Three-quarter arc with an arrowhead on the leading end, mirrored for
|
|
# the counter-clockwise variant.
|
|
var r: float = 11.0 * s
|
|
var segments: int = 14
|
|
var sweep: float = TAU * 0.75
|
|
var start: float = -PI * 0.5
|
|
var arc: PackedVector2Array = PackedVector2Array()
|
|
for i in range(segments + 1):
|
|
var t: float = float(i) / segments
|
|
var a: float = start + sweep * t * direction
|
|
arc.append(center + Vector2(cos(a), sin(a)) * r)
|
|
for i in range(segments):
|
|
NeonDraw.draw_glowing_line(self, arc[i], arc[i + 1], _active_color(), pulse, 0.34 * s, core_color)
|
|
|
|
var tip: Vector2 = arc[arc.size() - 1]
|
|
var tangent: Vector2 = (tip - arc[arc.size() - 2]).normalized()
|
|
var normal: Vector2 = Vector2(-tangent.y, tangent.x)
|
|
var head: float = 5.0 * s
|
|
NeonDraw.draw_glowing_line(self, tip, tip - tangent * head + normal * head, _active_color(), pulse, 0.34 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, tip, tip - tangent * head - normal * head, _active_color(), pulse, 0.34 * s, core_color)
|
|
|
|
|
|
# --- building glyphs, each echoing the placed structure's silhouette ------
|
|
|
|
func _draw_mothership_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# Radial flagship: a reactor core with six swept talons around it, the
|
|
# same silhouette as the placed structure.
|
|
for i in range(6):
|
|
var base: float = (float(i) / 6.0) * TAU
|
|
var talon: PackedVector2Array = PackedVector2Array()
|
|
for step in [0.0, 0.5, 1.0]:
|
|
var a: float = base + 0.72 * pow(step, 1.3)
|
|
talon.append(center + Vector2(cos(a), sin(a)) * lerpf(7.0, 16.0, step) * s)
|
|
for step in [1.0, 0.5, 0.0]:
|
|
var a: float = base + 0.72 * pow(step, 1.3) - (0.34 * (1.0 - step) + 0.05)
|
|
talon.append(center + Vector2(cos(a), sin(a)) * lerpf(7.0, 16.0, step) * s)
|
|
NeonDraw.draw_glowing_polygon(self, talon, _active_color(), pulse, core_color)
|
|
|
|
for radius in [6.5, 3.0]:
|
|
var ring: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
ring.append(center + Vector2(cos(a), sin(a)) * radius * s)
|
|
NeonDraw.draw_glowing_polygon(self, ring, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_ship_lab_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A vessel inside brackets: something being examined, not built.
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(0, -9) * s, center + Vector2(5, 5) * s,
|
|
center + Vector2(0, 2) * s, center + Vector2(-5, 5) * s,
|
|
]), _active_color(), pulse, core_color)
|
|
for sx in [-1.0, 1.0]:
|
|
var x: float = sx * 11.0 * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(x, -11 * s), center + Vector2(x, 11 * s),
|
|
_active_color(), pulse, 0.35 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(x, -11 * s), center + Vector2(x - sx * 4.0 * s, -11 * s),
|
|
_active_color(), pulse, 0.35 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(x, 11 * s), center + Vector2(x - sx * 4.0 * s, 11 * s),
|
|
_active_color(), pulse, 0.35 * s, core_color)
|
|
|
|
|
|
func _draw_workshop_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A wrench crossed with a girder. Nothing else in the set is a tool, and
|
|
# the meaning wanted is "things are mended here" - the crafter's cradle
|
|
# already owns "things are made here".
|
|
var arm: float = 11.0 * s
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-arm, arm),
|
|
center + Vector2(arm * 0.5, -arm * 0.5), _active_color(), pulse, 0.5 * s, core_color)
|
|
# The jaws.
|
|
NeonDraw.draw_glowing_path(self, PackedVector2Array([
|
|
center + Vector2(arm * 0.2, -arm * 0.95),
|
|
center + Vector2(arm * 0.95, -arm * 0.8),
|
|
center + Vector2(arm * 0.8, -arm * 0.1),
|
|
center + Vector2(arm * 0.15, -arm * 0.3),
|
|
]), _active_color(), pulse, 0.4 * s, core_color)
|
|
# The girder it is working on.
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-arm, -arm * 0.35),
|
|
center + Vector2(arm * 0.1, arm), _active_color(), pulse * 0.7, 0.35 * s, core_color)
|
|
|
|
|
|
func _draw_repair_drone_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A small hull with a tool arm out in front, so it reads as a tender
|
|
# rather than as another fighter.
|
|
var r: float = 9.0 * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(0.0, -r), center + Vector2(r * 0.9, 0.0),
|
|
center + Vector2(r * 0.6, r), center + Vector2(-r * 0.6, r),
|
|
center + Vector2(-r * 0.9, 0.0),
|
|
]), _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0.0, -r),
|
|
center + Vector2(0.0, -r * 1.9), _active_color(), pulse, 0.35 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-r * 0.5, -r * 1.9),
|
|
center + Vector2(r * 0.5, -r * 1.9), _active_color(), pulse, 0.35 * s, core_color)
|
|
|
|
|
|
func _draw_planning_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A tally board: a frame with three bars of different lengths inside it.
|
|
# Nothing else in the set is rectilinear and horizontal, and the meaning
|
|
# wanted is "how many of each", not "a place that builds things" - the
|
|
# crafter's cradle already owns that.
|
|
var w: float = 12.0 * s
|
|
var h: float = 11.0 * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(-w, -h), center + Vector2(w, -h),
|
|
center + Vector2(w, h), center + Vector2(-w, h),
|
|
]), _active_color(), pulse, core_color)
|
|
var lengths: Array = [0.82, 0.5, 0.66]
|
|
for i in range(lengths.size()):
|
|
var y: float = center.y + (float(i) - 1.0) * 6.0 * s
|
|
var left: float = center.x - w + 3.0 * s
|
|
NeonDraw.draw_glowing_line(self, Vector2(left, y),
|
|
Vector2(left + (2.0 * w - 6.0 * s) * float(lengths[i]), y),
|
|
_active_color(), pulse, 0.4 * s, core_color)
|
|
|
|
|
|
func _draw_outpost_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A watchtower with two vessels holding station off it. The hub's
|
|
# octagon already means "things are stationed here and go out from
|
|
# here"; this has to mean "things stay here and watch", so it is a
|
|
# narrow mast with a sensor head rather than a ring, and the two darts
|
|
# beside it are the garrison.
|
|
var mast_top: Vector2 = center + Vector2(0.0, -11.0) * s
|
|
var mast_foot: Vector2 = center + Vector2(0.0, 9.0) * s
|
|
NeonDraw.draw_glowing_line(self, mast_foot, mast_top, _active_color(), pulse, 0.5 * s, core_color)
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
mast_top + Vector2(0.0, -4.0) * s, mast_top + Vector2(4.0, 0.0) * s,
|
|
mast_top + Vector2(0.0, 4.0) * s, mast_top + Vector2(-4.0, 0.0) * s,
|
|
]), _active_color(), pulse, core_color)
|
|
# Footing, so the mast stands on something rather than floating.
|
|
NeonDraw.draw_glowing_line(self, mast_foot + Vector2(-6.0, 0.0) * s,
|
|
mast_foot + Vector2(6.0, 0.0) * s, _active_color(), pulse, 0.4 * s, core_color)
|
|
for side in [-1.0, 1.0]:
|
|
var at: Vector2 = center + Vector2(11.0 * side, -1.0) * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
at + Vector2(0.0, -4.0) * s, at + Vector2(2.6, 3.0) * s,
|
|
at + Vector2(-2.6, 3.0) * s,
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_transport_hub_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A ring of docking cradles with a freight box in the middle: reads as
|
|
# "things are stationed here and go out from here", which is what the hub
|
|
# does, and shares no silhouette with the crafter's open cradle.
|
|
var ring_r: float = 11.0 * s
|
|
var ring: PackedVector2Array = PackedVector2Array()
|
|
for i in range(8):
|
|
var a: float = (float(i) / 8.0) * TAU + PI / 8.0
|
|
ring.append(center + Vector2(cos(a), sin(a)) * ring_r)
|
|
NeonDraw.draw_glowing_polygon(self, ring, _active_color(), pulse, core_color)
|
|
|
|
var box: float = 4.2 * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(-box, -box), center + Vector2(box, -box),
|
|
center + Vector2(box, box), center + Vector2(-box, box),
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
# Three stubby berths, so the ring reads as occupied rather than as a
|
|
# plain octagon.
|
|
for i in range(3):
|
|
var a: float = (float(i) / 3.0) * TAU - PI / 2.0
|
|
var dir: Vector2 = Vector2(cos(a), sin(a))
|
|
NeonDraw.draw_glowing_line(self, center + dir * (box * 1.5),
|
|
center + dir * (ring_r - 1.5 * s), _active_color(), pulse, 0.35 * s, core_color)
|
|
|
|
|
|
func _draw_crafter_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# Reactor core, truss spine, and an open cradle holding a hull.
|
|
var core_centre: Vector2 = center + Vector2(-11, 0) * s
|
|
var core: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
core.append(core_centre + Vector2(cos(a), sin(a)) * 5.0 * s)
|
|
NeonDraw.draw_glowing_polygon(self, core, _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-6, 0) * s,
|
|
center + Vector2(-2, 0) * s, _active_color(), pulse, 0.35 * s, core_color)
|
|
|
|
var bay: Vector2 = center + Vector2(6, 0) * s
|
|
var cradle: PackedVector2Array = PackedVector2Array()
|
|
for i in range(13):
|
|
var a: float = lerpf(-0.25 * PI, 1.25 * PI, float(i) / 12.0)
|
|
cradle.append(bay + Vector2(cos(a), sin(a)) * 10.0 * s)
|
|
NeonDraw.draw_glowing_path(self, cradle, _active_color(), pulse, 0.4 * s, core_color)
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
bay + Vector2(0, -6) * s, bay + Vector2(-5, 4) * s, bay + Vector2(5, 4) * s,
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_command_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# Hex plotting rim with spokes to a diamond hub, echoing the placed
|
|
# structure's silhouette: half the ports carry a formation token.
|
|
var hex_r: float = 11.0 * s
|
|
var hub_r: float = 4.0 * s
|
|
var hex: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
hex.append(center + Vector2(cos(a), sin(a)) * hex_r)
|
|
NeonDraw.draw_glowing_polygon(self, hex, _active_color(), pulse, core_color)
|
|
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
var dir: Vector2 = Vector2(cos(a), sin(a))
|
|
NeonDraw.draw_glowing_line(self, center + dir * hub_r, center + dir * hex_r, _active_color(), pulse, 0.32 * s, core_color)
|
|
if i % 2 == 0:
|
|
var tip: Vector2 = center + dir * hex_r
|
|
var t: float = 2.6 * s
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
tip + Vector2(0, -t), tip + Vector2(t, 0), tip + Vector2(0, t), tip + Vector2(-t, 0),
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([
|
|
center + Vector2(0, -hub_r), center + Vector2(hub_r, 0),
|
|
center + Vector2(0, hub_r), center + Vector2(-hub_r, 0),
|
|
]), _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_extractor_icon(center: Vector2, pulse: float, s: float) -> void:
|
|
# A derrick standing on a planet's limb, with ore lifting off it - the
|
|
# one building that only works over a planet, so the planet is the point.
|
|
var limb: PackedVector2Array = PackedVector2Array()
|
|
for i in range(11):
|
|
var a: float = lerpf(-0.82 * PI, -0.18 * PI, float(i) / 10.0)
|
|
limb.append(center + (Vector2(0, 22) + Vector2(cos(a), sin(a)) * 17.0) * s)
|
|
NeonDraw.draw_glowing_path(self, limb, _active_color(), pulse, 0.45 * s, core_color)
|
|
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-6, 5) * s, center + Vector2(0, -9) * s, _active_color(), pulse, 0.4 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(6, 5) * s, center + Vector2(0, -9) * s, _active_color(), pulse, 0.4 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-3.5, -1) * s, center + Vector2(3.5, -1) * s, _active_color(), pulse, 0.35 * s, core_color)
|
|
# Shaft driven down through the limb - without it the derrick reads as a
|
|
# tower standing on a hill rather than as something mining the planet.
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(0, -1) * s, center + Vector2(0, 14) * s, _active_color(), pulse, 0.3 * s, core_color)
|
|
|
|
var ore_centre: Vector2 = center + Vector2(0, -15) * s
|
|
var ore: PackedVector2Array = PackedVector2Array()
|
|
for i in range(6):
|
|
var a: float = (float(i) / 6.0) * TAU + PI / 6.0
|
|
ore.append(ore_centre + Vector2(cos(a), sin(a)) * 4.0 * s)
|
|
NeonDraw.draw_glowing_polygon(self, ore, _active_color(), pulse, core_color)
|
|
|
|
|
|
func _draw_map(center: Vector2, pulse: float, s: float) -> void:
|
|
var w: float = 11.0 * s
|
|
var h: float = 9.0 * s
|
|
var outline: PackedVector2Array = PackedVector2Array([
|
|
center + Vector2(-w, -h), center + Vector2(w, -h * 0.7),
|
|
center + Vector2(w, h), center + Vector2(-w, h * 0.7),
|
|
])
|
|
NeonDraw.draw_glowing_polygon(self, outline, _active_color(), pulse, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(-w * 0.34, -h * 0.9), center + Vector2(-w * 0.34, h * 0.77), _active_color(), pulse, 0.4 * s, core_color)
|
|
NeonDraw.draw_glowing_line(self, center + Vector2(w * 0.34, -h * 0.77), center + Vector2(w * 0.34, h * 0.9), _active_color(), pulse, 0.4 * s, core_color)
|