Nothing under ui/ or vfx/ was marked @tool, so every hand-drawn control rendered as an empty rect in the editor. BuildMenu.tscn positions its buttons with hand-tuned pixel offsets (offset_left = -204) around a glyph identified only as `icon = 8` - laying out invisible boxes by number. @tool on the three scene-placed components that are safe to run in the editor, covering 23 of the 34 placements: IconButton (20 scenes), TouchJoystick (2), NeonPanel (1). Two of them mutate persisted properties on _ready, which under @tool would write computed values back into the .tscn every time the scene was opened, so both are guarded with Engine.is_editor_hint(): NeonPanel._fit_to_screen() rewrites all four offsets TouchJoystick._relayout() overwrites max_radius, which is @export IconButton's _gui_input is guarded too, so clicking a button on the editor canvas does not emit `pressed`. The autoload-heavy drawers (ResourceBar, PriceTag, QuestPanel, ResourceTransferList, SkillTreeView, FormationEditor - 6 to 16 GameState./Quests. references each) are left alone; they need their call sites guarded first. Verified: 172/172 headless, and a --headless --editor pass loads the project with no script errors. That pass only proves the scripts parse under @tool - it never opens a scene, so it does not exercise the in-editor _draw. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
969 lines
43 KiB
GDScript
969 lines
43 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 would silently repoint every button after it.
|
|
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 and neither of them
|
|
# looking anything like the structure they build.
|
|
MOTHERSHIP, CRAFTER, COMMAND_CENTER, EXTRACTOR, TRANSPORT_HUB, SHIP_LAB,
|
|
# Moving ore between the fleet and a building (#24). Two arrows passing
|
|
# each other: the single up/down arrows already mean "load" and "store"
|
|
# one way each, and this button is the place where both live.
|
|
TRANSFER,
|
|
# Ship-lab upgrade glyphs. These deliberately describe the effect rather
|
|
# than the building hosting it, so cards remain scannable at a glance.
|
|
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,
|
|
GAS_BOMBER, GAS_BOMB,
|
|
# Appended, not inserted alongside the other building icons above -
|
|
# inserting there would silently renumber every icon int already
|
|
# stored in existing scenes (#54).
|
|
GAS_BURNER,
|
|
# Same append-only rule (#57): a wide hauler hull, distinct from every
|
|
# vessel glyph above.
|
|
CARRIER_VESSEL,
|
|
# #58: LASER_VESSEL is the ship glyph, LASER_SHOT the action button -
|
|
# same two-icon split as GAS_BOMBER/GAS_BOMB above.
|
|
LASER_VESSEL, LASER_SHOT,
|
|
# #59 drew this as a warped ring, for a vessel then called a space-time
|
|
# distorder. #77 renamed it the Energy Propulser and the glyph is now an
|
|
# engine bell; the enum name stays put because renumbering it would
|
|
# silently renumber every icon int stored in every scene.
|
|
DISTORDER_VESSEL,
|
|
# #78: switching a converter off and on. Appended, like everything above -
|
|
# inserting anywhere earlier renumbers every icon int already stored in a
|
|
# scene file.
|
|
PAUSE, RESUME,
|
|
# #102: appended, like everything above - inserting anywhere earlier
|
|
# renumbers every icon int already stored in every scene.
|
|
OUTPOST,
|
|
# #106: appended, same rule.
|
|
PLANNING_CENTER,
|
|
# #116: the workshop and the drone it sends out. Appended, same rule.
|
|
WORKSHOP, REPAIR_DRONE,
|
|
# #140: the standing objective. Appended, same rule as everything above -
|
|
# inserting anywhere earlier renumbers every icon int already stored in
|
|
# every scene.
|
|
QUEST,
|
|
# #179: a fleet-wide thrust burst, distinct from the Propulser vessel icon.
|
|
DASH,
|
|
# Development tools: three sliders, used only by the editor-only sandbox
|
|
# button. Appended to preserve every serialized icon number above.
|
|
DEV_TOOLS,
|
|
# #193: a directional sensor dish. Appended: scene files store these as
|
|
# integers, so insertion would change every icon after it.
|
|
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.
|
|
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 menu.is_visible_in_tree() and not menu.is_ancestor_of(self):
|
|
return true
|
|
return false
|
|
|
|
|
|
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)
|