extends Control # ============================================================================ # MapPanel.gd - 右上角小镇地图弹窗 # ============================================================================ # 监听 HUD 地图入口,展示当前区域全图与关键建筑标识。 # ============================================================================ const PANEL_SIZE: Vector2 = Vector2(548, 452) const PANEL_MARGIN_TOP: float = 122.0 const PANEL_MARGIN_RIGHT: float = 56.0 const MAP_VIEW_SIZE: Vector2 = Vector2(500, 375) const MAP_WORLD_SIZE: Vector2 = Vector2(2560, 1920) const MAP_CAMERA_ZOOM: Vector2 = Vector2(MAP_VIEW_SIZE.x / MAP_WORLD_SIZE.x, MAP_VIEW_SIZE.y / MAP_WORLD_SIZE.y) const MINIMAP_EXCLUDED_NODE_NAMES: Array[String] = [ "Characters", "MultiplayerController", ] const TEXT_COLOR: Color = Color(0.188, 0.294, 0.424) const MUTED_COLOR: Color = Color(0.560, 0.639, 0.733) const ACCENT_COLOR: Color = Color(0.258824, 0.627451, 0.913725) const SOFT_BLUE: Color = Color(0.918, 0.961, 0.992, 0.94) const PIN_BLUE: Color = Color(0.244, 0.572, 0.862) const MAP_CONFIGS: Dictionary = { "Square": { "title": "小镇地图", "sourceNodes": [ "HDGrassBase", "GroundGrassLayer", "PathBaseLayer", "PathEdgeLayer", "PathEdgeCornerLayer", "PathEdgeJoinLayer", "PathCurbLayer", "YSortWorld", ], "markers": [ {"label": "码头", "destinationId": "square_dock", "icon": "anchor", "pos": Vector2(0.160, 0.395), "side": "right"}, {"label": "总部", "destinationId": "square_headquarters", "icon": "home", "pos": Vector2(0.505, 0.188), "side": "right"}, {"label": "广场", "destinationId": "square_center", "icon": "whale", "pos": Vector2(0.505, 0.515), "side": "right"}, {"label": "小屋", "destinationId": "square_cottage", "icon": "home", "pos": Vector2(0.830, 0.400), "side": "right"}, {"label": "工坊", "destinationId": "square_workshop", "icon": "tool", "pos": Vector2(0.752, 0.760), "side": "left"}, {"label": "公告", "destinationId": "square_notice", "icon": "notice", "pos": Vector2(0.288, 0.838), "side": "right"}, {"label": "入口", "destinationId": "square_work_zone_gate", "icon": "gate", "pos": Vector2(0.500, 0.900), "side": "right"}, ], }, "WorkZone": { "title": "打工区地图", "sourceNodes": [ "StageBackdrop", "RoadPavementLayer", "RoadAsphaltLayer", "RoadCrosswalkLayer", "RoadMarkingLayer", "YSortWorld", ], "markers": [ {"label": "商城", "destinationId": "work_mall", "icon": "home", "pos": Vector2(0.500, 0.180), "side": "right"}, {"label": "咖啡店", "destinationId": "work_cafe_gate", "icon": "home", "pos": Vector2(0.092, 0.620), "side": "right"}, {"label": "任务", "destinationId": "work_jobs", "icon": "notice", "pos": Vector2(0.304, 0.600), "side": "right"}, {"label": "课程", "destinationId": "work_courses", "icon": "notice", "pos": Vector2(0.694, 0.500), "side": "left"}, {"label": "AI站", "destinationId": "work_ai", "icon": "tool", "pos": Vector2(0.676, 0.785), "side": "left"}, {"label": "鲸币", "destinationId": "work_exchange", "icon": "whale", "pos": Vector2(0.920, 0.785), "side": "left"}, {"label": "入口", "destinationId": "work_entrance", "icon": "gate", "pos": Vector2(0.500, 0.900), "side": "right"}, ], }, "CafeInterior": { "title": "鲸鱼咖啡馆", "worldSize": Vector2(1536, 1024), "sourceNodes": [ "StageBackdrop", "CafeServiceHallBase", ], "markers": [ {"label": "服务台", "destinationId": "cafe_counter", "icon": "whale", "pos": Vector2(0.500, 0.465), "side": "right"}, {"label": "陪伴区", "destinationId": "cafe_companion", "icon": "notice", "pos": Vector2(0.240, 0.280), "side": "right"}, {"label": "出口", "destinationId": "cafe_entrance", "icon": "gate", "pos": Vector2(0.500, 0.855), "side": "right"}, ], }, "PersonalSpace": { "title": "我的房间", "worldSize": Vector2(1024, 768), "sourceNodes": [ "RoomBase", ], "markers": [], }, } var _panel: PanelContainer var _mapFrame: PanelContainer var _mapLayer: Control var _mapViewport: SubViewport var _mapWorld: Node2D var _mapCamera: Camera2D var _titleLabel: Label var _markerNodes: Array[Control] = [] var _isOpen: bool = false var _travelDestinations: Dictionary = {} var _travelLocked: bool = false var _destinationMenu: PopupMenu var _destinationMenuIds: Dictionary = {} func _ready() -> void: add_to_group("whaletown_escape_dismissible") mouse_filter = Control.MOUSE_FILTER_IGNORE _build_ui() set_panel_open(false) _subscribe_to_events() func _exit_tree() -> void: var eventSystem := _get_event_system() if eventSystem != null: eventSystem.call("disconnect_event", EventNames.HUD_MAP_TOGGLE, _on_map_toggle, self) func _notification(what: int) -> void: if what == NOTIFICATION_RESIZED and is_instance_valid(_panel): _anchor_panel() func _input(event: InputEvent) -> void: if get_viewport().is_input_handled(): return if not _isOpen: return if event is InputEventKey: var keyEvent := event as InputEventKey if keyEvent.pressed and not keyEvent.echo and keyEvent.keycode == KEY_ESCAPE: set_panel_open(false) get_viewport().set_input_as_handled() func set_panel_open(open: bool) -> void: _isOpen = open visible = _isOpen mouse_filter = Control.MOUSE_FILTER_STOP if _isOpen else Control.MOUSE_FILTER_IGNORE if is_instance_valid(_panel): _panel.visible = _isOpen if _isOpen: _rebuild_minimap_world() _load_travel_destinations() func is_panel_open() -> bool: return _isOpen func is_escape_dismissible() -> bool: return _isOpen func get_escape_priority() -> int: return 600 func request_escape_close() -> void: set_panel_open(false) func toggle_panel() -> void: set_panel_open(not _isOpen) func _build_ui() -> void: _panel = PanelContainer.new() _panel.name = "MapCard" _panel.custom_minimum_size = PANEL_SIZE _panel.mouse_filter = Control.MOUSE_FILTER_STOP _panel.add_theme_stylebox_override("panel", _create_panel_style()) add_child(_panel) _anchor_panel() var margin := MarginContainer.new() margin.add_theme_constant_override("margin_left", 24) margin.add_theme_constant_override("margin_top", 20) margin.add_theme_constant_override("margin_right", 24) margin.add_theme_constant_override("margin_bottom", 22) _panel.add_child(margin) var content := VBoxContainer.new() content.add_theme_constant_override("separation", 12) margin.add_child(content) content.add_child(_build_header()) content.add_child(_build_map_view()) _destinationMenu = PopupMenu.new() _destinationMenu.id_pressed.connect(_on_destination_menu_selected) add_child(_destinationMenu) func _build_header() -> Control: var header := HBoxContainer.new() header.custom_minimum_size = Vector2(0, 42) header.alignment = BoxContainer.ALIGNMENT_CENTER header.add_theme_constant_override("separation", 12) var icon := MapGlyph.new() icon.custom_minimum_size = Vector2(32, 32) icon.mouse_filter = Control.MOUSE_FILTER_IGNORE header.add_child(icon) var title := Label.new() title.name = "TitleLabel" _titleLabel = title title.text = "小镇地图" title.size_flags_horizontal = Control.SIZE_EXPAND_FILL title.add_theme_color_override("font_color", TEXT_COLOR) title.add_theme_font_size_override("font_size", 24) header.add_child(title) var destinationButton := Button.new() destinationButton.text = "目的地" destinationButton.tooltip_text = "查看全部快速传送目的地" destinationButton.custom_minimum_size = Vector2(76, 38) destinationButton.focus_mode = Control.FOCUS_NONE destinationButton.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND destinationButton.add_theme_font_size_override("font_size", 14) destinationButton.add_theme_color_override("font_color", ACCENT_COLOR) destinationButton.add_theme_stylebox_override("normal", _create_round_style(Color(0.902, 0.962, 0.995, 1.0), 14)) destinationButton.add_theme_stylebox_override("hover", _create_round_style(Color(0.818, 0.925, 0.980, 1.0), 14)) destinationButton.add_theme_stylebox_override("focus", StyleBoxEmpty.new()) destinationButton.pressed.connect(func() -> void: _show_destination_menu(destinationButton)) header.add_child(destinationButton) var closeButton := Button.new() closeButton.text = "×" closeButton.tooltip_text = "关闭地图" closeButton.custom_minimum_size = Vector2(42, 42) closeButton.focus_mode = Control.FOCUS_NONE closeButton.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND closeButton.add_theme_font_size_override("font_size", 28) closeButton.add_theme_color_override("font_color", MUTED_COLOR) closeButton.add_theme_color_override("font_hover_color", ACCENT_COLOR) closeButton.add_theme_color_override("font_pressed_color", ACCENT_COLOR.darkened(0.18)) closeButton.add_theme_color_override("font_disabled_color", Color(0.520, 0.600, 0.680, 0.55)) closeButton.add_theme_stylebox_override("normal", _create_round_style(SOFT_BLUE, 21)) closeButton.add_theme_stylebox_override("hover", _create_round_style(Color(0.858, 0.925, 0.980, 1.0), 21)) closeButton.add_theme_stylebox_override("pressed", _create_round_style(Color(0.778, 0.884, 0.965, 1.0), 21)) closeButton.add_theme_stylebox_override("focus", StyleBoxEmpty.new()) closeButton.pressed.connect(func() -> void: set_panel_open(false)) header.add_child(closeButton) return header func _build_map_view() -> Control: _mapFrame = PanelContainer.new() _mapFrame.custom_minimum_size = MAP_VIEW_SIZE _mapFrame.mouse_filter = Control.MOUSE_FILTER_STOP _mapFrame.add_theme_stylebox_override("panel", _create_map_frame_style()) _mapLayer = Control.new() _mapLayer.custom_minimum_size = MAP_VIEW_SIZE _mapLayer.clip_contents = true _mapLayer.mouse_filter = Control.MOUSE_FILTER_IGNORE _mapFrame.add_child(_mapLayer) var viewportContainer := SubViewportContainer.new() viewportContainer.name = "SquareMapViewportContainer" viewportContainer.set_anchors_preset(Control.PRESET_FULL_RECT) viewportContainer.stretch = true viewportContainer.mouse_filter = Control.MOUSE_FILTER_IGNORE _mapLayer.add_child(viewportContainer) _mapViewport = SubViewport.new() _mapViewport.name = "SquareMapViewport" _mapViewport.size = Vector2i(int(MAP_VIEW_SIZE.x), int(MAP_VIEW_SIZE.y)) _mapViewport.render_target_update_mode = SubViewport.UPDATE_WHEN_VISIBLE _mapViewport.world_2d = World2D.new() viewportContainer.add_child(_mapViewport) _mapWorld = Node2D.new() _mapWorld.name = "RenderedMapWorld" _mapViewport.add_child(_mapWorld) _mapCamera = Camera2D.new() _mapCamera.name = "MinimapCamera" _mapCamera.position = Vector2.ZERO _mapCamera.zoom = MAP_CAMERA_ZOOM _mapCamera.enabled = true _mapViewport.add_child(_mapCamera) var wash := ColorRect.new() wash.name = "MapSoftWash" wash.set_anchors_preset(Control.PRESET_FULL_RECT) wash.color = Color(1.0, 0.985, 0.930, 0.10) wash.mouse_filter = Control.MOUSE_FILTER_IGNORE _mapLayer.add_child(wash) _render_markers() return _mapFrame func _render_markers() -> void: for markerNode in _markerNodes: if is_instance_valid(markerNode): markerNode.queue_free() _markerNodes.clear() for marker in _current_markers(): var markerNode := _create_marker(marker) _markerNodes.append(markerNode) _mapLayer.add_child(markerNode) func _create_marker(marker: Dictionary) -> Control: var button := Button.new() button.name = "Marker%s" % str(marker.get("label", "")) button.text = "" button.focus_mode = Control.FOCUS_NONE button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND button.custom_minimum_size = Vector2(82, 34) button.add_theme_stylebox_override("normal", _create_marker_style(Color(1.0, 1.0, 1.0, 0.93), Color(0.650, 0.792, 0.910, 0.88))) button.add_theme_stylebox_override("hover", _create_marker_style(Color(0.925, 0.966, 0.996, 0.98), ACCENT_COLOR)) button.add_theme_stylebox_override("pressed", _create_marker_style(Color(0.858, 0.925, 0.980, 0.98), ACCENT_COLOR.darkened(0.05))) button.add_theme_stylebox_override("focus", StyleBoxEmpty.new()) var destinationId := str(marker.get("destinationId", "")).strip_edges() var destination: Dictionary = _travelDestinations.get(destinationId, {}) var unlocked := destinationId.is_empty() or _travelDestinations.is_empty() or bool(destination.get("unlocked", false)) button.disabled = not unlocked or _travelLocked button.tooltip_text = "点击快速传送" if unlocked else "首次到访后解锁" if not unlocked: button.modulate = Color(0.66, 0.69, 0.72, 0.72) button.pressed.connect(func() -> void: return) else: button.pressed.connect(func() -> void: _request_travel(destinationId)) var row := HBoxContainer.new() row.mouse_filter = Control.MOUSE_FILTER_IGNORE row.set_anchors_preset(Control.PRESET_FULL_RECT) row.offset_left = 8 row.offset_right = -9 row.alignment = BoxContainer.ALIGNMENT_CENTER row.add_theme_constant_override("separation", 5) button.add_child(row) var icon := MarkerGlyph.new() icon.set("iconName", str(marker.get("icon", "pin"))) icon.custom_minimum_size = Vector2(20, 20) icon.mouse_filter = Control.MOUSE_FILTER_IGNORE row.add_child(icon) var label := Label.new() label.mouse_filter = Control.MOUSE_FILTER_IGNORE label.text = ("🔒 " if not unlocked else "") + str(marker.get("label", "地点")) label.add_theme_color_override("font_color", TEXT_COLOR) label.add_theme_font_size_override("font_size", 14) row.add_child(label) var position := marker.get("pos", Vector2.ZERO) as Vector2 var buttonSize := button.custom_minimum_size var xOffset := 8.0 if str(marker.get("side", "right")) == "right" else -buttonSize.x - 8.0 button.position = Vector2( position.x * MAP_VIEW_SIZE.x + xOffset, position.y * MAP_VIEW_SIZE.y - buttonSize.y * 0.5 ) return button func _load_travel_destinations() -> void: var api := get_node_or_null("/root/ApiClient") if api == null: return api.call("get_json", "/world/travel-destinations", func(success: bool, response: Dictionary, _error: Dictionary) -> void: if not success: return var data_variant: Variant = response.get("data", []) if not (data_variant is Array): return _travelDestinations.clear() for entry in data_variant as Array: if entry is Dictionary: var destination: Dictionary = entry _travelDestinations[str(destination.get("id", ""))] = destination _render_markers() , true) func _show_destination_menu(origin: Control) -> void: if not is_instance_valid(_destinationMenu): return _destinationMenu.clear() _destinationMenuIds.clear() if _travelDestinations.is_empty(): _destinationMenu.add_item("正在读取目的地…", 0) _destinationMenu.set_item_disabled(0, true) else: var destinations: Array[Dictionary] = [] for destination_variant in _travelDestinations.values(): if destination_variant is Dictionary: destinations.append(destination_variant as Dictionary) destinations.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: var map_order := {"whale_port": 0, "work_zone": 1, "whale_cafe": 2, "personal_space": 3} var order_a := int(map_order.get(str(a.get("mapId", "")), 9)) var order_b := int(map_order.get(str(b.get("mapId", "")), 9)) if order_a != order_b: return order_a < order_b return str(a.get("label", "")).naturalnocasecmp_to(str(b.get("label", ""))) < 0 ) var current_map := "" var menu_id := 1 for destination in destinations: var map_id := str(destination.get("mapId", "")) if map_id != current_map: if not current_map.is_empty(): _destinationMenu.add_separator() _destinationMenu.add_item("— %s —" % _map_label(map_id), menu_id) _destinationMenu.set_item_disabled(_destinationMenu.item_count - 1, true) menu_id += 1 current_map = map_id var unlocked := bool(destination.get("unlocked", false)) _destinationMenu.add_item(("" if unlocked else "🔒 ") + str(destination.get("label", "地点")), menu_id) _destinationMenu.set_item_disabled(_destinationMenu.item_count - 1, not unlocked or _travelLocked) _destinationMenuIds[menu_id] = str(destination.get("id", "")) menu_id += 1 _destinationMenu.reset_size() _destinationMenu.position = Vector2i(origin.get_screen_position() + Vector2(0.0, origin.size.y)) _destinationMenu.popup() func _on_destination_menu_selected(menu_id: int) -> void: var destination_id := str(_destinationMenuIds.get(menu_id, "")) if not destination_id.is_empty(): _request_travel(destination_id) func _request_travel(destinationId: String) -> void: if destinationId.is_empty() or _travelLocked: return var socialManager := get_node_or_null("/root/SocialManager") if socialManager == null or not socialManager.has_method("request_travel"): return _travelLocked = true _render_markers() socialManager.call("request_travel", destinationId, Callable(self, "_on_travel_authorized")) func _on_travel_authorized(success: bool, destination: Dictionary) -> void: if not success: await get_tree().create_timer(1.0).timeout _travelLocked = false _render_markers() return var mapId := str(destination.get("mapId", "")) var position := _scene_position_for_destination(mapId, Vector2(float(destination.get("x", 0.0)), float(destination.get("y", 0.0)))) var sceneName: String = str({"whale_port": "square", "work_zone": "work_zone", "whale_cafe": "cafe_interior", "personal_space": "personal_space"}.get(mapId, "")) if sceneName.is_empty(): await get_tree().create_timer(1.0).timeout _travelLocked = false _render_markers() return set_panel_open(false) SceneManager.set_next_destination_id(str(destination.get("id", ""))) SceneManager.set_next_scene_position(position) SceneManager.change_scene(sceneName) func _scene_position_for_destination(map_id: String, map_position: Vector2) -> Vector2: # 服务端旅行目录使用小地图坐标;游戏场景则以地图中心为原点。 var origin := { "whale_port": Vector2(1280, 960), "work_zone": Vector2(1280, 960), "whale_cafe": Vector2(768, 512), "personal_space": Vector2(768, 512), }.get(map_id, Vector2.ZERO) as Vector2 return map_position - origin func _map_label(map_id: String) -> String: return { "whale_port": "中心广场", "work_zone": "打工区", "whale_cafe": "鲸鱼咖啡馆", "personal_space": "我的房间", }.get(map_id, map_id) func _anchor_panel() -> void: _panel.set_anchors_preset(Control.PRESET_TOP_RIGHT) _panel.offset_left = -PANEL_SIZE.x - PANEL_MARGIN_RIGHT _panel.offset_top = PANEL_MARGIN_TOP _panel.offset_right = -PANEL_MARGIN_RIGHT _panel.offset_bottom = PANEL_MARGIN_TOP + PANEL_SIZE.y _panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN func _subscribe_to_events() -> void: var eventSystem := _get_event_system() if eventSystem == null: push_warning("MapPanel: EventSystem autoload is not available.") return eventSystem.call("connect_event", EventNames.HUD_MAP_TOGGLE, _on_map_toggle, self) func _on_map_toggle(_data: Variant = null) -> void: toggle_panel() func _rebuild_minimap_world() -> void: if not is_instance_valid(_mapWorld): return _clear_children(_mapWorld) var sourceScene := _find_source_scene() if sourceScene == null: push_warning("MapPanel: source scene for minimap is unavailable.") return _apply_scene_config(sourceScene) for nodeName in _current_source_node_names(sourceScene): var sourceNode := sourceScene.get_node_or_null(nodeName) if sourceNode == null: continue var clone := sourceNode.duplicate(0) if clone == null: continue _remove_excluded_minimap_nodes(clone) _mapWorld.add_child(clone) func _find_source_scene() -> Node: var node: Node = self while node != null: if MAP_CONFIGS.has(str(node.name)): return node node = node.get_parent() return get_tree().current_scene func _apply_scene_config(sourceScene: Node) -> void: var config := _map_config_for_scene(sourceScene) if is_instance_valid(_titleLabel): _titleLabel.text = str(config.get("title", "区域地图")) _configure_map_camera(config) _render_markers() func _configure_map_camera(config: Dictionary) -> void: if not is_instance_valid(_mapCamera): return var worldSize := config.get("worldSize", MAP_WORLD_SIZE) as Vector2 if worldSize.x <= 0.0 or worldSize.y <= 0.0: worldSize = MAP_WORLD_SIZE var zoomScale := minf(MAP_VIEW_SIZE.x / worldSize.x, MAP_VIEW_SIZE.y / worldSize.y) _mapCamera.zoom = Vector2.ONE * zoomScale func _current_source_node_names(sourceScene: Node) -> Array: var config := _map_config_for_scene(sourceScene) var nodeNames: Array = config.get("sourceNodes", []) return nodeNames func _current_markers() -> Array: var config := _map_config_for_scene(_find_source_scene()) var markers: Array = config.get("markers", []) return markers func _map_config_for_scene(sourceScene: Node) -> Dictionary: if sourceScene != null: var sceneName := str(sourceScene.name) if MAP_CONFIGS.has(sceneName): return MAP_CONFIGS[sceneName] return MAP_CONFIGS["Square"] func _remove_excluded_minimap_nodes(root: Node) -> void: for nodeName in MINIMAP_EXCLUDED_NODE_NAMES: var node := root.find_child(nodeName, true, false) if node != null and node.get_parent() != null: node.get_parent().remove_child(node) node.queue_free() func _clear_children(container: Node) -> void: for child in container.get_children(): container.remove_child(child) child.queue_free() func _create_panel_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = Color(1.0, 1.0, 1.0, 0.948) style.corner_radius_top_left = 28 style.corner_radius_top_right = 28 style.corner_radius_bottom_left = 28 style.corner_radius_bottom_right = 28 style.shadow_color = Color(0.12549, 0.282353, 0.407843, 0.18) style.shadow_size = 22 style.shadow_offset = Vector2(0, 9) return style func _create_map_frame_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = Color(0.918, 0.961, 0.992, 0.96) style.border_color = Color(0.431, 0.733, 0.929, 0.86) style.border_width_left = 2 style.border_width_top = 2 style.border_width_right = 2 style.border_width_bottom = 2 style.corner_radius_top_left = 16 style.corner_radius_top_right = 16 style.corner_radius_bottom_left = 16 style.corner_radius_bottom_right = 16 return style func _create_marker_style(color: Color, borderColor: Color) -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = color style.border_color = borderColor style.border_width_left = 2 style.border_width_top = 2 style.border_width_right = 2 style.border_width_bottom = 2 style.corner_radius_top_left = 18 style.corner_radius_top_right = 18 style.corner_radius_bottom_left = 18 style.corner_radius_bottom_right = 18 style.shadow_color = Color(0.078, 0.223, 0.360, 0.14) style.shadow_size = 7 style.shadow_offset = Vector2(0, 3) return style func _create_round_style(color: Color, radius: int) -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = color style.corner_radius_top_left = radius style.corner_radius_top_right = radius style.corner_radius_bottom_left = radius style.corner_radius_bottom_right = radius return style func _get_event_system() -> Node: return get_node_or_null("/root/EventSystem") class MapGlyph: extends Control func _draw() -> void: var color := Color(0.258824, 0.627451, 0.913725) var width := 2.2 draw_polyline(PackedVector2Array([ Vector2(6, 9), Vector2(12, 7), Vector2(19, 9), Vector2(26, 7), Vector2(26, 25), Vector2(19, 27), Vector2(12, 25), Vector2(6, 27), Vector2(6, 9) ]), color, width, true) draw_line(Vector2(12, 8), Vector2(12, 24), color, 1.6, true) draw_line(Vector2(19, 10), Vector2(19, 26), color, 1.6, true) class MarkerGlyph: extends Control var iconName: String = "pin" func _draw() -> void: var color := Color(0.258824, 0.627451, 0.913725) var center := size * 0.5 match iconName: "anchor": _draw_anchor(center, color) "home": _draw_home(center, color) "whale": _draw_whale(center, color) "tool": _draw_tool(center, color) "notice": _draw_notice(center, color) "gate": _draw_gate(center, color) _: draw_circle(center, 5.0, color) func _draw_anchor(center: Vector2, color: Color) -> void: draw_line(center + Vector2(0, -7), center + Vector2(0, 7), color, 2.0, true) draw_circle(center + Vector2(0, -7), 2.8, color) draw_arc(center + Vector2(0, 2), 7.0, 0.20, PI - 0.20, 24, color, 2.0, true) draw_line(center + Vector2(-7, 3), center + Vector2(-10, 0), color, 2.0, true) draw_line(center + Vector2(7, 3), center + Vector2(10, 0), color, 2.0, true) func _draw_home(center: Vector2, color: Color) -> void: draw_polyline(PackedVector2Array([ center + Vector2(-8, -1), center + Vector2(0, -8), center + Vector2(8, -1) ]), color, 2.0, true) draw_rect(Rect2(center + Vector2(-6, -1), Vector2(12, 10)), color, false, 2.0) draw_line(center + Vector2(-1, 9), center + Vector2(-1, 3), color, 2.0, true) func _draw_whale(center: Vector2, color: Color) -> void: draw_arc(center + Vector2(-1, 2), 8.0, PI, TAU, 28, color, 2.0, true) draw_line(center + Vector2(6, 0), center + Vector2(11, -4), color, 2.0, true) draw_line(center + Vector2(6, 0), center + Vector2(11, 4), color, 2.0, true) draw_circle(center + Vector2(-4, 0), 1.2, color) func _draw_tool(center: Vector2, color: Color) -> void: draw_line(center + Vector2(-7, 7), center + Vector2(7, -7), color, 2.4, true) draw_arc(center + Vector2(7, -7), 5.0, PI * 0.1, PI * 1.3, 18, color, 2.0, true) draw_circle(center + Vector2(-7, 7), 2.4, color) func _draw_notice(center: Vector2, color: Color) -> void: draw_rect(Rect2(center + Vector2(-8, -7), Vector2(16, 13)), color, false, 2.0) draw_line(center + Vector2(-5, -2), center + Vector2(5, -2), color, 1.7, true) draw_line(center + Vector2(-5, 2), center + Vector2(2, 2), color, 1.7, true) func _draw_gate(center: Vector2, color: Color) -> void: draw_rect(Rect2(center + Vector2(-9, -3), Vector2(18, 11)), color, false, 2.0) draw_line(center + Vector2(0, -3), center + Vector2(0, 8), color, 1.7, true) draw_line(center + Vector2(-11, 8), center + Vector2(11, 8), color, 2.0, true)