forked from xiangwang25/whale-town-front-v2
feat: expand multiplayer, chat, and release support
- add network NPC synchronization and dialogue interactions - add world bulletin publishing and display - improve authentication, session refresh, and appearance sync - synchronize player direction and movement animations - improve input focus and progressive Web loading - add macOS/Windows builds and deployment configuration - include required fonts, shaders, and runtime assets
This commit is contained in:
@@ -16,11 +16,12 @@ const WORK_ZONE_CAFE_RETURN_POSITION: Vector2 = Vector2(-1093, 560)
|
||||
const CAFE_DOOR_POSITION: Vector2 = Vector2(0, 392)
|
||||
const WORLD_TEXT_THEME = preload("res://assets/ui/world_text_theme.tres")
|
||||
const COMPANION_NAMEPLATE_RENDER_SCALE: float = 0.5
|
||||
const COMPANION_NAMEPLATE_FONT_SIZE: int = 24
|
||||
const COMPANION_NAMEPLATE_VISUAL_HEIGHT: int = 22
|
||||
const COMPANION_NAMEPLATE_VISUAL_MIN_WIDTH: int = 76
|
||||
const COMPANION_NAMEPLATE_VISUAL_MAX_WIDTH: int = 118
|
||||
const COMPANION_NAMEPLATE_VISUAL_CHAR_WIDTH: int = 12
|
||||
const COMPANION_NAMEPLATE_FONT_SIZE: int = 16
|
||||
const COMPANION_NAMEPLATE_VISUAL_HEIGHT: int = 15
|
||||
const COMPANION_NAMEPLATE_VISUAL_MIN_WIDTH: int = 64
|
||||
const COMPANION_NAMEPLATE_VISUAL_MAX_WIDTH: int = 100
|
||||
const COMPANION_NAMEPLATE_FONT_ZH: FontFile = preload("res://assets/fonts/fusion-pixel-12px/fusion-pixel-12px-proportional-zh_hans.ttf.woff2")
|
||||
const COMPANION_NAMEPLATE_FONT_LATIN: FontFile = preload("res://assets/fonts/fusion-pixel-12px/fusion-pixel-12px-proportional-latin.ttf.woff2")
|
||||
const NPC_NAMEPLATE_OFFSET_Y: float = -136.0
|
||||
const HIRED_PLAYER_NAMEPLATE_OFFSET_Y: float = -96.0
|
||||
|
||||
@@ -36,6 +37,7 @@ const HIRED_PLAYER_NAMEPLATE_OFFSET_Y: float = -96.0
|
||||
|
||||
var _isChangingScene: bool = false
|
||||
var _lastRecruitmentClickMsec: int = 0
|
||||
var _companionNicknameFont: FontFile
|
||||
|
||||
func _ready() -> void:
|
||||
_align_service_occupants()
|
||||
@@ -252,34 +254,20 @@ func _configure_companion_nameplate(label: Label, personaName: String, offsetY:
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
label.add_theme_color_override("font_color", Color(0.12, 0.20, 0.24, 1.0))
|
||||
label.add_theme_color_override("font_shadow_color", Color(1.0, 1.0, 1.0, 0.85))
|
||||
label.add_theme_constant_override("shadow_offset_x", 0)
|
||||
label.add_theme_constant_override("shadow_offset_y", 1)
|
||||
label.add_theme_color_override("font_color", Color(0.09, 0.25, 0.31, 1.0))
|
||||
label.add_theme_color_override("font_outline_color", Color(1.0, 0.965, 0.88, 0.98))
|
||||
label.add_theme_constant_override("outline_size", 3)
|
||||
label.add_theme_font_override("font", _get_companion_nickname_font())
|
||||
label.add_theme_font_size_override("font_size", COMPANION_NAMEPLATE_FONT_SIZE)
|
||||
label.add_theme_stylebox_override("normal", _create_companion_nameplate_style())
|
||||
label.add_theme_stylebox_override("normal", StyleBoxEmpty.new())
|
||||
|
||||
func _companion_nameplate_visual_width(displayName: String) -> int:
|
||||
var estimatedWidth := displayName.length() * COMPANION_NAMEPLATE_VISUAL_CHAR_WIDTH + 28
|
||||
return clampi(estimatedWidth, COMPANION_NAMEPLATE_VISUAL_MIN_WIDTH, COMPANION_NAMEPLATE_VISUAL_MAX_WIDTH)
|
||||
var measuredWidth := _get_companion_nickname_font().get_string_size(displayName, HORIZONTAL_ALIGNMENT_LEFT, -1, COMPANION_NAMEPLATE_FONT_SIZE).x
|
||||
var visualWidth := ceili(measuredWidth * COMPANION_NAMEPLATE_RENDER_SCALE + 20.0)
|
||||
return clampi(visualWidth, COMPANION_NAMEPLATE_VISUAL_MIN_WIDTH, COMPANION_NAMEPLATE_VISUAL_MAX_WIDTH)
|
||||
|
||||
func _create_companion_nameplate_style() -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color(1.0, 0.988, 0.955, 0.96)
|
||||
style.border_color = Color(0.18, 0.34, 0.38, 0.92)
|
||||
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 = 12
|
||||
style.corner_radius_top_right = 12
|
||||
style.corner_radius_bottom_left = 12
|
||||
style.corner_radius_bottom_right = 12
|
||||
style.content_margin_left = 12
|
||||
style.content_margin_top = 4
|
||||
style.content_margin_right = 12
|
||||
style.content_margin_bottom = 4
|
||||
style.shadow_color = Color(0.05, 0.08, 0.09, 0.18)
|
||||
style.shadow_size = 4
|
||||
style.shadow_offset = Vector2(0, 2)
|
||||
return style
|
||||
func _get_companion_nickname_font() -> FontFile:
|
||||
if _companionNicknameFont == null:
|
||||
_companionNicknameFont = COMPANION_NAMEPLATE_FONT_ZH.duplicate() as FontFile
|
||||
_companionNicknameFont.fallbacks = [COMPANION_NAMEPLATE_FONT_LATIN]
|
||||
return _companionNicknameFont
|
||||
|
||||
@@ -7,6 +7,7 @@ extends Node
|
||||
# ============================================================================
|
||||
|
||||
const REMOTE_PLAYER_SCENE: PackedScene = preload("res://scenes/characters/remote_player.tscn")
|
||||
const NETWORK_NPC_SCENE: PackedScene = preload("res://scenes/characters/network_npc.tscn")
|
||||
const CHAT_BUBBLE_SCENE: PackedScene = preload("res://scenes/ui/ChatBubble.tscn")
|
||||
const DEFAULT_MAP_ID: String = "whale_port"
|
||||
const POSITION_SEND_INTERVAL: float = 0.12
|
||||
@@ -19,19 +20,26 @@ const PRIVATE_CHAT_FORWARD_DOT_THRESHOLD: float = 0.25
|
||||
@export var map_id: String = DEFAULT_MAP_ID
|
||||
@export var local_player_path: NodePath = NodePath("../Characters/Players/Player")
|
||||
@export var remote_players_root_path: NodePath = NodePath("../Characters/Players/RemotePlayers")
|
||||
@export var network_npcs_root_path: NodePath = NodePath("../Characters/Npcs")
|
||||
|
||||
var _local_player: Node2D
|
||||
var _remote_players_root: Node2D
|
||||
var _network_npcs_root: Node2D
|
||||
var _remote_players: Dictionary = {}
|
||||
var _network_npcs: Dictionary = {}
|
||||
var _pending_remote_players: Dictionary = {}
|
||||
var _last_sent_position: Vector2 = Vector2.INF
|
||||
var _last_sent_at_msec: int = 0
|
||||
var _last_sent_direction: String = ""
|
||||
var _last_sent_movement_state: String = ""
|
||||
var _movement_sequence: int = 0
|
||||
var _last_private_chat_interaction_msec: int = 0
|
||||
var _last_friend_request_interaction_msec: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
_local_player = get_node_or_null(local_player_path) as Node2D
|
||||
_remote_players_root = get_node_or_null(remote_players_root_path) as Node2D
|
||||
_network_npcs_root = get_node_or_null(network_npcs_root_path) as Node2D
|
||||
|
||||
if _local_player == null:
|
||||
push_warning("MapMultiplayerController: local player not found.")
|
||||
@@ -46,6 +54,9 @@ func _ready() -> void:
|
||||
eventSystem.call("connect_event", EventNames.PLAYER_MOVED, _on_local_player_moved, self)
|
||||
eventSystem.call("connect_event", EventNames.CHAT_LOGIN_SUCCESS, _on_chat_login_success, self)
|
||||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYERS_SNAPSHOT_READY, _on_remote_players_snapshot_ready, self)
|
||||
eventSystem.call("connect_event", EventNames.NPC_SNAPSHOT_READY, _on_npc_snapshot_ready, self)
|
||||
eventSystem.call("connect_event", EventNames.NPC_ACTION_STARTED, _on_npc_action_started, self)
|
||||
eventSystem.call("connect_event", EventNames.NPC_ACTION_COMPLETED, _on_npc_action_completed, self)
|
||||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_POSITION_UPDATED, _on_remote_player_position_updated, self)
|
||||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_JOINED, _on_remote_player_joined, self)
|
||||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_LEFT, _on_remote_player_left, self)
|
||||
@@ -78,6 +89,9 @@ func _exit_tree() -> void:
|
||||
eventSystem.call("disconnect_event", EventNames.PLAYER_MOVED, _on_local_player_moved, self)
|
||||
eventSystem.call("disconnect_event", EventNames.CHAT_LOGIN_SUCCESS, _on_chat_login_success, self)
|
||||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYERS_SNAPSHOT_READY, _on_remote_players_snapshot_ready, self)
|
||||
eventSystem.call("disconnect_event", EventNames.NPC_SNAPSHOT_READY, _on_npc_snapshot_ready, self)
|
||||
eventSystem.call("disconnect_event", EventNames.NPC_ACTION_STARTED, _on_npc_action_started, self)
|
||||
eventSystem.call("disconnect_event", EventNames.NPC_ACTION_COMPLETED, _on_npc_action_completed, self)
|
||||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_POSITION_UPDATED, _on_remote_player_position_updated, self)
|
||||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_JOINED, _on_remote_player_joined, self)
|
||||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_LEFT, _on_remote_player_left, self)
|
||||
@@ -175,7 +189,9 @@ func _on_chat_login_success(_data: Dictionary) -> void:
|
||||
func _on_local_player_moved(data: Dictionary) -> void:
|
||||
var position_variant: Variant = data.get("position", null)
|
||||
if position_variant is Vector2:
|
||||
_send_position(position_variant as Vector2)
|
||||
var direction := str(data.get("direction", _get_local_direction()))
|
||||
var movementState := str(data.get("movement_state", "walk"))
|
||||
_send_position(position_variant as Vector2, direction, movementState)
|
||||
|
||||
func _send_world_ready() -> void:
|
||||
if _local_player == null:
|
||||
@@ -183,13 +199,16 @@ func _send_world_ready() -> void:
|
||||
var chatManager := _get_chat_manager()
|
||||
if chatManager == null or not chatManager.has_method("mark_world_ready"):
|
||||
return
|
||||
chatManager.call("mark_world_ready", _get_map_id(), _local_player.global_position)
|
||||
chatManager.call("mark_world_ready", _get_map_id(), _local_player.global_position, _get_local_direction(), "idle", _movement_sequence)
|
||||
|
||||
func _send_position(position: Vector2, force: bool = false) -> void:
|
||||
func _send_position(position: Vector2, direction: String, movementState: String, force: bool = false) -> void:
|
||||
var normalizedDirection := _normalize_direction(direction)
|
||||
var normalizedMovementState := _normalize_movement_state(movementState)
|
||||
var stateChanged := normalizedDirection != _last_sent_direction or normalizedMovementState != _last_sent_movement_state
|
||||
var now := Time.get_ticks_msec()
|
||||
if not force and now - _last_sent_at_msec < int(POSITION_SEND_INTERVAL * 1000.0):
|
||||
if not force and not stateChanged and now - _last_sent_at_msec < int(POSITION_SEND_INTERVAL * 1000.0):
|
||||
return
|
||||
if not force and _last_sent_position != Vector2.INF and _last_sent_position.distance_to(position) < MIN_POSITION_DELTA:
|
||||
if not force and not stateChanged and _last_sent_position != Vector2.INF and _last_sent_position.distance_to(position) < MIN_POSITION_DELTA:
|
||||
return
|
||||
|
||||
var chatManager := _get_chat_manager()
|
||||
@@ -200,7 +219,10 @@ func _send_position(position: Vector2, force: bool = false) -> void:
|
||||
|
||||
_last_sent_position = position
|
||||
_last_sent_at_msec = now
|
||||
chatManager.call("update_player_position", position.x, position.y, _get_map_id())
|
||||
_last_sent_direction = normalizedDirection
|
||||
_last_sent_movement_state = normalizedMovementState
|
||||
_movement_sequence += 1
|
||||
chatManager.call("update_player_position", position.x, position.y, _get_map_id(), normalizedDirection, normalizedMovementState, _movement_sequence)
|
||||
|
||||
func _on_remote_player_joined(data: Dictionary) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
@@ -210,8 +232,8 @@ func _on_remote_player_joined(data: Dictionary) -> void:
|
||||
return
|
||||
var remote_player := _ensure_remote_player(user_id, data)
|
||||
var position_variant: Variant = data.get("position", null)
|
||||
if remote_player != null and position_variant is Vector2 and remote_player.has_method("update_position"):
|
||||
remote_player.call("update_position", position_variant as Vector2)
|
||||
if remote_player != null and position_variant is Vector2:
|
||||
_update_remote_player_position(remote_player, position_variant as Vector2, data)
|
||||
|
||||
func _on_remote_players_snapshot_ready(data: Dictionary) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
@@ -230,8 +252,8 @@ func _on_remote_players_snapshot_ready(data: Dictionary) -> void:
|
||||
seen_user_ids[user_id] = true
|
||||
var remote_player := _ensure_remote_player(user_id, player_data)
|
||||
var position_variant: Variant = player_data.get("position", null)
|
||||
if remote_player != null and position_variant is Vector2 and remote_player.has_method("update_position"):
|
||||
remote_player.call("update_position", position_variant as Vector2)
|
||||
if remote_player != null and position_variant is Vector2:
|
||||
_update_remote_player_position(remote_player, position_variant as Vector2, player_data)
|
||||
|
||||
for user_id_variant in _remote_players.keys().duplicate():
|
||||
var user_id := str(user_id_variant)
|
||||
@@ -246,6 +268,70 @@ func _on_remote_players_snapshot_ready(data: Dictionary) -> void:
|
||||
if not seen_user_ids.has(pendingUserId):
|
||||
_pending_remote_players.erase(pendingUserId)
|
||||
|
||||
func _on_npc_snapshot_ready(data: Dictionary) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
return
|
||||
if _network_npcs_root == null:
|
||||
return
|
||||
|
||||
var seenNpcIds: Dictionary = {}
|
||||
var npcsVariant: Variant = data.get("npcs", [])
|
||||
if npcsVariant is Array:
|
||||
for npcVariant in npcsVariant:
|
||||
if not (npcVariant is Dictionary):
|
||||
continue
|
||||
var npcData: Dictionary = npcVariant
|
||||
var npcId := str(npcData.get("npc_id", npcData.get("npcId", ""))).strip_edges()
|
||||
if npcId.is_empty():
|
||||
continue
|
||||
seenNpcIds[npcId] = true
|
||||
var networkNpc := _ensure_network_npc(npcId)
|
||||
if networkNpc != null and networkNpc.has_method("apply_snapshot"):
|
||||
networkNpc.call("apply_snapshot", npcData)
|
||||
|
||||
for npcIdVariant in _network_npcs.keys().duplicate():
|
||||
var npcId := str(npcIdVariant)
|
||||
if seenNpcIds.has(npcId):
|
||||
continue
|
||||
var networkNpc := _network_npcs.get(npcId) as Node
|
||||
_network_npcs.erase(npcId)
|
||||
if is_instance_valid(networkNpc):
|
||||
networkNpc.queue_free()
|
||||
|
||||
func _ensure_network_npc(npcId: String) -> Node2D:
|
||||
if _network_npcs.has(npcId):
|
||||
var existingNpc := _network_npcs.get(npcId) as Node2D
|
||||
if is_instance_valid(existingNpc):
|
||||
return existingNpc
|
||||
_network_npcs.erase(npcId)
|
||||
|
||||
var networkNpc := NETWORK_NPC_SCENE.instantiate() as Node2D
|
||||
if networkNpc == null:
|
||||
return null
|
||||
networkNpc.name = "NetworkNpc_%s" % npcId
|
||||
networkNpc.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
_network_npcs_root.add_child(networkNpc)
|
||||
_network_npcs[npcId] = networkNpc
|
||||
return networkNpc
|
||||
|
||||
func _on_npc_action_started(data: Dictionary) -> void:
|
||||
_apply_npc_action(data, "apply_action_started")
|
||||
|
||||
func _on_npc_action_completed(data: Dictionary) -> void:
|
||||
_apply_npc_action(data, "apply_action_completed")
|
||||
|
||||
func _apply_npc_action(data: Dictionary, methodName: String) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
return
|
||||
var npcId := str(data.get("npc_id", data.get("npcId", ""))).strip_edges()
|
||||
if npcId.is_empty():
|
||||
return
|
||||
var networkNpc := _network_npcs.get(npcId) as Node
|
||||
if networkNpc == null:
|
||||
networkNpc = _ensure_network_npc(npcId)
|
||||
if networkNpc != null and networkNpc.has_method(methodName):
|
||||
networkNpc.call(methodName, data)
|
||||
|
||||
func _on_remote_player_position_updated(data: Dictionary) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
return
|
||||
@@ -260,8 +346,8 @@ func _on_remote_player_position_updated(data: Dictionary) -> void:
|
||||
return
|
||||
|
||||
var remote_player := _ensure_remote_player(user_id, data)
|
||||
if remote_player != null and remote_player.has_method("update_position"):
|
||||
remote_player.call("update_position", position_variant as Vector2)
|
||||
if remote_player != null:
|
||||
_update_remote_player_position(remote_player, position_variant as Vector2, data)
|
||||
|
||||
func _on_remote_player_left(data: Dictionary) -> void:
|
||||
if not _is_event_for_current_map(data):
|
||||
@@ -357,8 +443,8 @@ func _on_remote_skin_ready(data: Dictionary) -> void:
|
||||
_pending_remote_players.erase(userId)
|
||||
var remotePlayer := _ensure_remote_player(userId, pendingData)
|
||||
var positionVariant: Variant = pendingData.get("position", null)
|
||||
if remotePlayer != null and positionVariant is Vector2 and remotePlayer.has_method("update_position"):
|
||||
remotePlayer.call("update_position", positionVariant as Vector2)
|
||||
if remotePlayer != null and positionVariant is Vector2:
|
||||
_update_remote_player_position(remotePlayer, positionVariant as Vector2, pendingData)
|
||||
|
||||
func _on_remote_skin_failed(data: Dictionary) -> void:
|
||||
var skinId := str(data.get("skin_id", "")).strip_edges()
|
||||
@@ -372,14 +458,14 @@ func _on_remote_skin_failed(data: Dictionary) -> void:
|
||||
continue
|
||||
_pending_remote_players.erase(userId)
|
||||
var fallbackData := pendingData.duplicate(true)
|
||||
fallbackData["skin_id"] = ""
|
||||
fallbackData["skinId"] = ""
|
||||
fallbackData["skin_id"] = "classic_whale"
|
||||
fallbackData["skinId"] = "classic_whale"
|
||||
fallbackData.erase("skin_asset")
|
||||
fallbackData.erase("skinAsset")
|
||||
var remotePlayer := _ensure_remote_player(userId, fallbackData)
|
||||
var positionVariant: Variant = pendingData.get("position", null)
|
||||
if remotePlayer != null and positionVariant is Vector2 and remotePlayer.has_method("update_position"):
|
||||
remotePlayer.call("update_position", positionVariant as Vector2)
|
||||
if remotePlayer != null and positionVariant is Vector2:
|
||||
_update_remote_player_position(remotePlayer, positionVariant as Vector2, pendingData)
|
||||
|
||||
func _find_private_chat_target() -> Node2D:
|
||||
if _local_player == null or _remote_players.is_empty():
|
||||
@@ -436,9 +522,31 @@ func _apply_remote_player_metadata(remote_player: Node2D, data: Dictionary) -> v
|
||||
var username := str(data.get("username", "")).strip_edges()
|
||||
if not username.is_empty():
|
||||
remote_player.set("username", username)
|
||||
if data.has("skin_id") or data.has("skinId") or data.has("skin_asset") or data.has("skinAsset") or data.has("avatar_id") or data.has("avatarId") or data.has("cafe_companion") or data.has("cafeCompanion"):
|
||||
if remote_player.has_method("setup"):
|
||||
remote_player.call("setup", data)
|
||||
if remote_player.has_method("update_metadata"):
|
||||
remote_player.call("update_metadata", data)
|
||||
|
||||
func _update_remote_player_position(remotePlayer: Node2D, position: Vector2, data: Dictionary) -> void:
|
||||
if not remotePlayer.has_method("update_position"):
|
||||
return
|
||||
remotePlayer.call(
|
||||
"update_position",
|
||||
position,
|
||||
str(data.get("direction", "")),
|
||||
str(data.get("movement_state", data.get("movementState", "walk"))),
|
||||
int(data.get("sequence", -1))
|
||||
)
|
||||
|
||||
func _get_local_direction() -> String:
|
||||
if _local_player != null:
|
||||
return _normalize_direction(str(_local_player.get("lastDirection")))
|
||||
return "down"
|
||||
|
||||
func _normalize_direction(value: String) -> String:
|
||||
var normalized := value.strip_edges().to_lower()
|
||||
return normalized if normalized in ["down", "up", "right", "left"] else "down"
|
||||
|
||||
func _normalize_movement_state(value: String) -> String:
|
||||
return "walk" if value.strip_edges().to_lower() == "walk" else "idle"
|
||||
|
||||
func _resolve_chat_bubble_target(data: Dictionary) -> Node2D:
|
||||
var from_user := str(data.get("from_user", data.get("username", ""))).strip_edges()
|
||||
|
||||
@@ -20,6 +20,9 @@ const CAMERA_LIMIT_BOTTOM: int = 960
|
||||
func _ready() -> void:
|
||||
_apply_spawn_point()
|
||||
_configure_camera()
|
||||
var chatManager := get_node_or_null("/root/ChatManager")
|
||||
if chatManager != null and chatManager.has_method("is_guest_mode") and bool(chatManager.call("is_guest_mode")):
|
||||
player.set_spectator_mode(true)
|
||||
|
||||
func _apply_spawn_point() -> void:
|
||||
var spawnName: String = SceneManager.get_next_spawn_name()
|
||||
|
||||
@@ -146,6 +146,9 @@ disabled = true
|
||||
[node name="RemotePlayers" type="Node2D" parent="YSortWorld/Characters/Players" unique_id=2071839484]
|
||||
y_sort_enabled = true
|
||||
|
||||
[node name="Npcs" type="Node2D" parent="YSortWorld/Characters"]
|
||||
y_sort_enabled = true
|
||||
|
||||
[node name="CafeWhaleBaristaNpc" parent="YSortWorld/Characters" unique_id=1188569491 instance=ExtResource("8_barista_npc")]
|
||||
position = Vector2(-472, -291)
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://c8ubnxut51f0r" path="res://assets/maps/square/v1/props/bottom_entrance_left_task_props_v2_hd_clean.png" id="12_lefttask"]
|
||||
[ext_resource type="Texture2D" uid="uid://b4bvp0r5hua1k" path="res://assets/maps/square/v1/props/bottom_entrance_right_service_props_v2_hd_clean.png" id="13_rightsvc"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/characters/player.tscn" id="14_u1t8b"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/characters/npc.tscn" id="17_qog0y"]
|
||||
[ext_resource type="Texture2D" uid="uid://c0kgmsaach8wh" path="res://assets/maps/square/v1/props/foliage_v13_concept_broadleaf_single/broadleaf_tree_v13_clean.png" id="19_edt5w"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/characters/crayfish_npc.tscn" id="20_lemld"]
|
||||
[ext_resource type="Texture2D" uid="uid://q3vldth6fyvw" path="res://assets/maps/square/v1/props/foliage_v14_concept_evergreen_single/evergreen_tree_v14_clean.png" id="20_rixdf"]
|
||||
[ext_resource type="Texture2D" uid="uid://cbqj51mpelsr4" path="res://assets/maps/square/v1/props/top_fence_v1/top_fence_v1_clean.png" id="22_jagxe"]
|
||||
[ext_resource type="Texture2D" uid="uid://csxvgs1v5puvt" path="res://assets/maps/square/v1/props/guild_left_tree_flowers_v1/guild_left_tree_flowers_v1_clean.png" id="24_8hf0h"]
|
||||
@@ -35,6 +33,7 @@
|
||||
[ext_resource type="Script" uid="uid://dxupavbgcw3tw" path="res://scenes/Maps/MapMultiplayerController.gd" id="38_multiplayer"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/ui/PlayerHud.tscn" id="39_playerhud"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/ui/FriendListPanel.tscn" id="40_friendpanel"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/ui/WorldBulletinPanel.tscn" id="45_world_bulletin"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/ui/SettingsPanel.tscn" id="41_settingspanel"]
|
||||
[ext_resource type="Script" uid="uid://dr6uk7m4fsr4l" path="res://scenes/Maps/ScenePortal.gd" id="42_sceneportal"]
|
||||
[ext_resource type="Script" uid="uid://biu0igl6133q5" path="res://scenes/Maps/Square.gd" id="43_square"]
|
||||
@@ -134,6 +133,9 @@ layer = 10
|
||||
|
||||
[node name="FriendListPanel" parent="UILayer" unique_id=914934559 instance=ExtResource("40_friendpanel")]
|
||||
|
||||
[node name="WorldBulletinPanel" parent="UILayer" instance=ExtResource("45_world_bulletin")]
|
||||
z_index = 1000
|
||||
|
||||
[node name="SettingsPanel" parent="UILayer" unique_id=588068795 instance=ExtResource("41_settingspanel")]
|
||||
|
||||
[node name="HDGrassBase" type="Sprite2D" parent="." unique_id=1853750890]
|
||||
@@ -385,20 +387,6 @@ y_sort_enabled = true
|
||||
[node name="Npcs" type="Node2D" parent="YSortWorld/Characters" unique_id=1114010880]
|
||||
y_sort_enabled = true
|
||||
|
||||
[node name="GuildReceptionNpc" parent="YSortWorld/Characters/Npcs" unique_id=442345899 instance=ExtResource("17_qog0y")]
|
||||
texture_filter = 1
|
||||
position = Vector2(-199, -515)
|
||||
npcName = "范鲸晶"
|
||||
dialogue = "欢迎来到 WhaleTown 广场。上方是公会大厅,南边是小镇入口,左边通往码头。"
|
||||
showNameplate = true
|
||||
nameplateOffsetY = -72.0
|
||||
|
||||
[node name="DockGuideNpc" parent="YSortWorld/Characters/Npcs" unique_id=626857126 instance=ExtResource("20_lemld")]
|
||||
texture_filter = 1
|
||||
position = Vector2(-825, 437)
|
||||
showNameplate = true
|
||||
nameplateOffsetY = -66.0
|
||||
|
||||
[node name="MultiplayerController" type="Node" parent="YSortWorld" unique_id=1810372571]
|
||||
script = ExtResource("38_multiplayer")
|
||||
|
||||
|
||||
@@ -241,6 +241,9 @@ position = Vector2(0, 921)
|
||||
y_sort_enabled = true
|
||||
position = Vector2(0, -1)
|
||||
|
||||
[node name="Npcs" type="Node2D" parent="YSortWorld/Characters"]
|
||||
y_sort_enabled = true
|
||||
|
||||
[node name="MultiplayerController" type="Node" parent="YSortWorld" unique_id=1024843959]
|
||||
script = ExtResource("26_multiplayer")
|
||||
map_id = "work_zone"
|
||||
|
||||
Reference in New Issue
Block a user