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, } # 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. @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: 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() func _handle_press(is_pressed: bool, at_position: Vector2) -> void: if is_pressed: pressed_down = true queue_redraw() 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 var square: PackedVector2Array = PackedVector2Array([ Vector2(margin, margin), Vector2(rect_size.x - margin, margin), Vector2(rect_size.x - margin, rect_size.y - margin), Vector2(margin, rect_size.y - margin), ]) NeonDraw.draw_glowing_polygon(self, square, _active_color(), pulse, core_color) if highlighted: # 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. var inset: float = 10.0 * s var focus: PackedVector2Array = PackedVector2Array([ Vector2(inset, inset), Vector2(rect_size.x - inset, inset), Vector2(rect_size.x - inset, rect_size.y - inset), Vector2(inset, rect_size.y - inset), ]) NeonDraw.draw_glowing_polygon(self, focus, 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) 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_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_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_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)