Files
whale-town-front-v2/scenes/ui/MapPanel.gd
2026-07-22 13:41:28 +08:00

682 lines
26 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
if sceneName == "personal_space":
SceneManager.clear_room_visit_context()
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)