模板脚本文件
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
extends Node
|
||||
|
||||
const FEDORA_ICON: Texture2D = preload("res://assets/items/hats/icons/fedora.png")
|
||||
const HEADPHONES_ICON: Texture2D = preload("res://assets/items/hats/icons/headphones.png")
|
||||
const PIRATE_HAT_ICON: Texture2D = preload("res://assets/items/hats/icons/pirate_hat.png")
|
||||
const SHERIFF_HAT_ICON: Texture2D = preload("res://assets/items/hats/icons/sheriff_hat.png")
|
||||
const SOMBRERO_ICON: Texture2D = preload("res://assets/items/hats/icons/sombrero.png")
|
||||
const WIZARD_HAT_ICON: Texture2D = preload("res://assets/items/hats/icons/wizard_hat.png")
|
||||
const BACKPACK_ICON: Texture2D = preload("res://assets/items/backpacks/icons/backpack.png")
|
||||
const SWORD_ICON: Texture2D = preload("res://assets/items/weapons/icons/sword.png")
|
||||
const SWORD_BIG_ICON: Texture2D = preload("res://assets/items/weapons/icons/sword_big.png")
|
||||
const AXE_ICON: Texture2D = preload("res://assets/items/weapons/icons/axe.png")
|
||||
const CHICKEN_LEG_ICON: Texture2D = preload("res://assets/items/misc/icons/chicken_leg.png")
|
||||
const BONE_ICON: Texture2D = preload("res://assets/items/misc/icons/bone.png")
|
||||
const CHALICE_ICON: Texture2D = preload("res://assets/items/misc/icons/chalice.png")
|
||||
|
||||
var items: Dictionary = {}
|
||||
|
||||
|
||||
func _ready():
|
||||
_create_sample_items()
|
||||
|
||||
|
||||
func get_item(item_id: String) -> Item:
|
||||
return items.get(item_id)
|
||||
|
||||
|
||||
func get_all_items() -> Dictionary:
|
||||
return items
|
||||
|
||||
|
||||
func _create_sample_items():
|
||||
_create_hat_item("fedora", "Fedora", "A timeless felt fedora.", "res://scenes/items/hats/fedora.tscn", FEDORA_ICON)
|
||||
_create_hat_item(
|
||||
"headphones",
|
||||
"Headphones",
|
||||
"Comfortable over-ear headphones.",
|
||||
"res://scenes/items/hats/headphones.tscn",
|
||||
HEADPHONES_ICON
|
||||
)
|
||||
_create_hat_item(
|
||||
"pirate_hat",
|
||||
"Pirate Hat",
|
||||
"A weathered hat for a daring pirate.",
|
||||
"res://scenes/items/hats/pirate_hat.tscn",
|
||||
PIRATE_HAT_ICON
|
||||
)
|
||||
_create_hat_item(
|
||||
"sheriff_hat",
|
||||
"Sheriff Hat",
|
||||
"A frontier hat with a sheriff badge.",
|
||||
"res://scenes/items/hats/sheriff_hat.tscn",
|
||||
SHERIFF_HAT_ICON
|
||||
)
|
||||
_create_hat_item(
|
||||
"sombrero", "Sombrero", "A broad and colorful sombrero.", "res://scenes/items/hats/sombrero.tscn", SOMBRERO_ICON
|
||||
)
|
||||
_create_hat_item(
|
||||
"wizard_hat",
|
||||
"Wizard Hat",
|
||||
"A pointed hat filled with arcane style.",
|
||||
"res://scenes/items/hats/wizard_hat.tscn",
|
||||
WIZARD_HAT_ICON
|
||||
)
|
||||
_create_item(
|
||||
"backpack",
|
||||
"Backpack",
|
||||
"A sturdy backpack worn on the back.",
|
||||
Item.ItemType.BACKPACK,
|
||||
"res://scenes/items/backpacks/backpack.tscn",
|
||||
BACKPACK_ICON,
|
||||
true,
|
||||
60
|
||||
)
|
||||
_create_item(
|
||||
"sword",
|
||||
"Sword",
|
||||
"A balanced hand sword.",
|
||||
Item.ItemType.WEAPON,
|
||||
"res://scenes/items/weapons/sword.tscn",
|
||||
SWORD_ICON,
|
||||
true,
|
||||
80
|
||||
)
|
||||
_create_item(
|
||||
"sword_big",
|
||||
"Big Sword",
|
||||
"A large two-handed sword.",
|
||||
Item.ItemType.WEAPON,
|
||||
"res://scenes/items/weapons/sword_big.tscn",
|
||||
SWORD_BIG_ICON,
|
||||
true,
|
||||
120
|
||||
)
|
||||
_create_item(
|
||||
"axe",
|
||||
"Small Axe",
|
||||
"A compact axe with a sharp steel head.",
|
||||
Item.ItemType.WEAPON,
|
||||
"res://scenes/items/weapons/axe.tscn",
|
||||
AXE_ICON,
|
||||
true,
|
||||
95
|
||||
)
|
||||
_create_item(
|
||||
"chicken_leg",
|
||||
"Chicken Leg",
|
||||
"A cooked chicken leg.",
|
||||
Item.ItemType.MISC,
|
||||
"res://scenes/items/misc/chicken_leg.tscn",
|
||||
CHICKEN_LEG_ICON,
|
||||
false,
|
||||
5
|
||||
)
|
||||
_create_item(
|
||||
"bone",
|
||||
"Bone",
|
||||
"A weathered bone.",
|
||||
Item.ItemType.MISC,
|
||||
"res://scenes/items/misc/bone.tscn",
|
||||
BONE_ICON,
|
||||
false,
|
||||
2
|
||||
)
|
||||
_create_item(
|
||||
"chalice",
|
||||
"Chalice",
|
||||
"A decorative golden chalice.",
|
||||
Item.ItemType.MISC,
|
||||
"res://scenes/items/misc/chalice.tscn",
|
||||
CHALICE_ICON,
|
||||
false,
|
||||
35
|
||||
)
|
||||
|
||||
|
||||
func _create_item(
|
||||
item_id: String,
|
||||
item_name: String,
|
||||
item_description: String,
|
||||
item_type: Item.ItemType,
|
||||
scene_path: String,
|
||||
item_icon: Texture2D,
|
||||
equipable: bool,
|
||||
item_value: int
|
||||
) -> void:
|
||||
var item := Item.new()
|
||||
item.id = item_id
|
||||
item.name = item_name
|
||||
item.description = item_description
|
||||
item.item_type = item_type
|
||||
item.rarity = Item.ItemRarity.COMMON
|
||||
item.stackable = not equipable
|
||||
item.max_stack = 99 if item.stackable else 1
|
||||
item.value = item_value
|
||||
item.icon = item_icon
|
||||
item.scene_path = scene_path
|
||||
if equipable:
|
||||
item.context_options.append(Item.ContextOptions.EQUIP)
|
||||
item.context_options.append(Item.ContextOptions.DROP)
|
||||
items[item.id] = item
|
||||
|
||||
|
||||
func _create_hat_item(
|
||||
item_id: String, item_name: String, item_description: String, scene_path: String, item_icon: Texture2D
|
||||
) -> void:
|
||||
var hat := Item.new()
|
||||
hat.id = item_id
|
||||
hat.name = item_name
|
||||
hat.description = item_description
|
||||
hat.item_type = Item.ItemType.HAT
|
||||
hat.rarity = Item.ItemRarity.COMMON
|
||||
hat.stackable = false
|
||||
hat.value = 25
|
||||
hat.icon = item_icon
|
||||
hat.scene_path = scene_path
|
||||
hat.context_options.append(Item.ContextOptions.EQUIP)
|
||||
hat.context_options.append(Item.ContextOptions.DROP)
|
||||
items[hat.id] = hat
|
||||
@@ -0,0 +1 @@
|
||||
uid://bsraoqq3rst1j
|
||||
@@ -0,0 +1,202 @@
|
||||
extends Node
|
||||
|
||||
signal player_connected(peer_id, player_info)
|
||||
signal server_disconnected
|
||||
|
||||
const SERVER_ADDRESS: String = "127.0.0.1"
|
||||
const SERVER_PORT: int = 8080
|
||||
const MAX_PLAYERS: int = 10
|
||||
const MAX_NICK_LENGTH := 24
|
||||
const MAX_ADDRESS_LENGTH := 253
|
||||
|
||||
var players = {}
|
||||
var player_info = {"nick": "host", "skin": Character.SkinColor.BLUE}
|
||||
var _session_active := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
multiplayer.server_disconnected.connect(_on_server_disconnected)
|
||||
multiplayer.connection_failed.connect(_on_connection_failed)
|
||||
multiplayer.peer_disconnected.connect(_on_player_disconnected)
|
||||
multiplayer.peer_connected.connect(_on_player_connected)
|
||||
multiplayer.connected_to_server.connect(_on_connected_ok)
|
||||
|
||||
|
||||
func start_host(nickname: String, skin_color_str: String):
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
var error = peer.create_server(SERVER_PORT, MAX_PLAYERS)
|
||||
if error:
|
||||
return error
|
||||
|
||||
peer.host.compress(ENetConnection.COMPRESS_RANGE_CODER)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
_session_active = true
|
||||
|
||||
player_info["nick"] = sanitize_nickname(nickname, "Host_" + str(multiplayer.get_unique_id()))
|
||||
player_info["skin"] = skin_str_to_e(skin_color_str)
|
||||
|
||||
if DisplayServer.get_name() == "headless":
|
||||
return
|
||||
|
||||
players[1] = player_info
|
||||
player_connected.emit(1, player_info)
|
||||
|
||||
|
||||
func join_game(nickname: String, skin_color_str: String, address: String = SERVER_ADDRESS):
|
||||
address = sanitize_address(address)
|
||||
if address.is_empty():
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
var error = peer.create_client(address, SERVER_PORT)
|
||||
if error:
|
||||
return error
|
||||
|
||||
peer.host.compress(ENetConnection.COMPRESS_RANGE_CODER)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
_session_active = true
|
||||
|
||||
player_info["nick"] = sanitize_nickname(nickname, "Player_" + str(multiplayer.get_unique_id()))
|
||||
player_info["skin"] = skin_str_to_e(skin_color_str)
|
||||
|
||||
|
||||
func _on_connected_ok():
|
||||
var peer_id = multiplayer.get_unique_id()
|
||||
players[peer_id] = player_info
|
||||
player_connected.emit(peer_id, player_info)
|
||||
_register_player.rpc_id(1, player_info)
|
||||
|
||||
|
||||
func _on_player_connected(id):
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
for peer_id in players:
|
||||
_sync_registered_player.rpc_id(id, peer_id, players[peer_id])
|
||||
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _register_player(new_player_info):
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
if not (new_player_info is Dictionary):
|
||||
return
|
||||
var new_player_id = multiplayer.get_remote_sender_id()
|
||||
if new_player_id == 0:
|
||||
return
|
||||
if players.has(new_player_id):
|
||||
return
|
||||
var sanitized_info = sanitize_player_info(new_player_info, "Player_" + str(new_player_id))
|
||||
players[new_player_id] = sanitized_info
|
||||
player_connected.emit(new_player_id, sanitized_info)
|
||||
_sync_registered_player.rpc(new_player_id, sanitized_info)
|
||||
|
||||
|
||||
func _on_player_disconnected(id):
|
||||
players.erase(id)
|
||||
|
||||
|
||||
func _on_connection_failed():
|
||||
_finish_session()
|
||||
|
||||
|
||||
func _on_server_disconnected():
|
||||
_finish_session()
|
||||
|
||||
|
||||
func leave_game() -> void:
|
||||
var peer := multiplayer.multiplayer_peer
|
||||
if peer:
|
||||
peer.close()
|
||||
_finish_session()
|
||||
|
||||
|
||||
func _finish_session() -> void:
|
||||
var had_session := _session_active or multiplayer.multiplayer_peer != null or not players.is_empty()
|
||||
_session_active = false
|
||||
multiplayer.multiplayer_peer = null
|
||||
players.clear()
|
||||
if had_session:
|
||||
server_disconnected.emit()
|
||||
|
||||
|
||||
func skin_str_to_e(s):
|
||||
match str(s).strip_edges().to_lower():
|
||||
"blue":
|
||||
return Character.SkinColor.BLUE
|
||||
"yellow":
|
||||
return Character.SkinColor.YELLOW
|
||||
"green":
|
||||
return Character.SkinColor.GREEN
|
||||
"red":
|
||||
return Character.SkinColor.RED
|
||||
_:
|
||||
return Character.SkinColor.BLUE
|
||||
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_registered_player(peer_id: int, registered_player_info: Dictionary):
|
||||
if multiplayer.is_server():
|
||||
return
|
||||
if players.has(peer_id):
|
||||
return
|
||||
var sanitized_info = sanitize_player_info(registered_player_info, "Player_" + str(peer_id))
|
||||
players[peer_id] = sanitized_info
|
||||
player_connected.emit(peer_id, sanitized_info)
|
||||
|
||||
|
||||
func sanitize_player_info(info: Dictionary, fallback_nick: String) -> Dictionary:
|
||||
return {
|
||||
"nick": sanitize_nickname(str(info.get("nick", "")), fallback_nick),
|
||||
"skin": sanitize_skin_value(info.get("skin", Character.SkinColor.BLUE))
|
||||
}
|
||||
|
||||
|
||||
func sanitize_nickname(nickname: String, fallback: String) -> String:
|
||||
var clean := ""
|
||||
var last_was_space := false
|
||||
for i in range(nickname.length()):
|
||||
var codepoint := nickname.unicode_at(i)
|
||||
if codepoint <= 31 or codepoint == 127:
|
||||
if not last_was_space:
|
||||
clean += " "
|
||||
last_was_space = true
|
||||
continue
|
||||
|
||||
var character := nickname.substr(i, 1)
|
||||
if character == " ":
|
||||
if last_was_space:
|
||||
continue
|
||||
last_was_space = true
|
||||
else:
|
||||
last_was_space = false
|
||||
clean += character
|
||||
|
||||
clean = clean.strip_edges()
|
||||
if clean.is_empty():
|
||||
clean = fallback.strip_edges()
|
||||
if clean.length() > MAX_NICK_LENGTH:
|
||||
clean = clean.substr(0, MAX_NICK_LENGTH).strip_edges()
|
||||
if clean.is_empty():
|
||||
clean = "Player"
|
||||
return clean
|
||||
|
||||
|
||||
func sanitize_address(address: String) -> String:
|
||||
var clean = address.strip_edges()
|
||||
if clean.is_empty():
|
||||
return SERVER_ADDRESS
|
||||
if clean.length() > MAX_ADDRESS_LENGTH:
|
||||
return ""
|
||||
if clean.contains("://") or clean.contains("/") or clean.contains("\\") or clean.contains(":"):
|
||||
return ""
|
||||
return clean
|
||||
|
||||
|
||||
func sanitize_skin_value(value) -> Character.SkinColor:
|
||||
if value is int:
|
||||
match value:
|
||||
Character.SkinColor.BLUE, Character.SkinColor.YELLOW, Character.SkinColor.GREEN, Character.SkinColor.RED:
|
||||
return value
|
||||
_:
|
||||
return Character.SkinColor.BLUE
|
||||
return skin_str_to_e(str(value))
|
||||
@@ -0,0 +1 @@
|
||||
uid://cedubc87s78fo
|
||||
Reference in New Issue
Block a user