Merge branch 'develop' into 'master'

优化代码,准备重写GameLoopManager.gd

See merge request paper-chemis-community/game!7
This commit is contained in:
2026-05-18 09:53:29 +08:00
22 changed files with 474 additions and 207 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
.godot/ .godot/
.vscode/ .vscode/
.idea/
+31 -25
View File
@@ -14,38 +14,44 @@ game/
- project.godot - project.godot
- icon.svg - icon.svg
- assets/ - assets/
- fonts/ - fonts/
- AlibabaPuHuiTi-3-65-Medium.ttf # 阿里巴巴普惠体 - AlibabaPuHuiTi-3-65-Medium.ttf # 阿里巴巴普惠体
- translation/ - translation/
- trans.csv - trans.csv
- scenes/ - scenes/
- menus/ - menus/
- main_menu.tscn - main_menu.tscn
- settings.tscn - settings.tscn
- game/ - game/
- game.tscn - game.tscn
- card.tscn - card.tscn
- scripts/ - scripts/
- autoload/ - autoload/
- GameManager.gd # 游戏管理 - GameManager.gd # 游戏管理
- DownloadManager.gd # 下载管理 - DownloadManager.gd # 下载管理
- MultiGame.gd # 多人游戏功能 - SceneManager.gd # 场景管理
- SceneManager.gd # 场景管理 - GameLoopManager.gd # 游戏循环管理
- main_menu/ - main_menu/
- main_menu.gd - main_menu.gd
- join_game_ui.gd - join_game_ui.gd
- create_game_ui.gd - create_game_ui.gd
- game/ - game/
- game.gd - game.gd
- card.gd - card.gd
- settings/ - settings/
- settings.gd - settings.gd
``` ```
## 如何运行 ## 如何运行
请先运行数据后端,在游戏设置中输入后端 URL(包含端口号和 `http://``https://` 前缀),或者选择本地已有的数据源,然后创建游戏或加入游戏开始游玩。 请先运行数据后端,在游戏设置中输入后端 URL(包含端口号和 `http://``https://` 前缀),或者选择本地已有的数据源,然后创建游戏或加入游戏开始游玩。
## 部分游戏逻辑介绍
### 游戏循环
游戏循环管理器(GameLoopManager.gd)负责游戏游戏循环的管理。
## 最佳实践 ## 最佳实践
本项目目前正在使用 Godot 4.6.2 进行开发。开发用语言为 GDScript。 本项目目前正在使用 Godot 4.6.2 进行开发。开发用语言为 GDScript。
@@ -56,4 +62,4 @@ game/
- 除连接了信号或 HTTPRequest、MultiplayerAPI 的函数外,任何函数都不应该以下划线(`_`)开头。 - 除连接了信号或 HTTPRequest、MultiplayerAPI 的函数外,任何函数都不应该以下划线(`_`)开头。
- 函数之间只需间隔一行,无需间隔两行。 - 函数之间既可间隔一行,也可间隔两行。建议函数间隔两行,部分间隔一行的函数为遗留问题。
+317
View File
@@ -0,0 +1,317 @@
extends Node
var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new()
var max_player_num: int
var player_num: int
# 玩家数据字典 [peer_id: {"name": str, "hand": Array, "score": int, "is_ready": bool}]
var players_data: Dictionary = {}
var current_turn: int = 1 # 当前回合数
var current_player_id: int = 1 # 当前行动玩家的 peer_id
var game_started: bool = false
var game_phase: String = "waiting" # waiting, playing, ended
var deck: Array = [] # 游戏牌组
func create_server(playern: int) -> void:
max_player_num = playern
player_num = 1
if peer.create_server(GameManager.port, playern) != OK:
print("Failed to create server on port: ", GameManager.port)
return
setup_multiplayer()
# 服务器自己也是玩家
players_data[1] = {
"name": "Host",
"hand": [],
"score": 0,
"is_ready": false
}
print("Server created on port: ", GameManager.port)
func create_client(ip: String) -> void:
if peer.create_client(ip, GameManager.port) != OK:
print("Failed to connect to server: ", ip)
return
setup_multiplayer()
print("Connecting to server: ", ip)
func setup_multiplayer() -> void:
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
multiplayer.connected_to_server.connect(_on_connected_to_server)
multiplayer.connection_failed.connect(_on_connection_failed)
func _on_connected_to_server() -> void:
print("Connected to server successfully!")
func _on_connection_failed() -> void:
print("Failed to connect to server")
func _on_peer_connected(id: int) -> void:
print("Peer connected: ", id)
add_player(id)
# 如果游戏未开始且达到最大玩家数,询问是否开始游戏
if !game_started and players_data.size() >= max_player_num:
check_all_players_ready()
func _on_peer_disconnected(id: int):
print("Peer disconnected: ", id)
remove_player(id)
# 如果当前玩家断开,切换到下一个玩家
if id == current_player_id and game_started:
next_turn()
func add_player(id: int) -> void:
# 只有服务器才能添加玩家
if multiplayer.is_server():
players_data[id] = {
"name": "Player_" + str(id),
"hand": [],
"score": 0,
"is_ready": false
}
player_num = players_data.size()
# 广播新玩家加入
broadcast_player_list()
print("Player added: ", id, " Total players: ", player_num)
func remove_player(id: int) -> void:
if multiplayer.is_server() and players_data.has(id):
players_data.erase(id)
player_num = players_data.size()
broadcast_player_list()
print("Player removed: ", id)
# RPC: 设置玩家准备状态
@rpc("any_peer", "call_remote", "reliable")
func set_player_ready(is_ready: bool) -> void:
var peer_id = multiplayer.get_remote_sender_id()
if players_data.has(peer_id):
players_data[peer_id]["is_ready"] = is_ready
broadcast_player_list()
if is_ready:
check_all_players_ready()
# RPC: 设置玩家名称
@rpc("any_peer", "call_remote", "reliable")
func set_player_name(name: String) -> void:
var peer_id = multiplayer.get_remote_sender_id()
if players_data.has(peer_id):
players_data[peer_id]["name"] = name
broadcast_player_list()
# RPC: 开始游戏(仅主机调用)
@rpc("call_remote", "reliable")
func start_game() -> void:
if multiplayer.is_server():
if players_data.size() < 2:
print("Need at least 2 players to start game")
return
game_started = true
game_phase = "playing"
current_turn = 1
initialize_deck()
deal_initial_cards()
# 随机选择第一个玩家
var player_ids = players_data.keys()
current_player_id = player_ids[randi() % player_ids.size()]
print("Game started! Current player: ", current_player_id)
broadcast_game_state()
# 初始化牌组
func initialize_deck() -> void:
deck = []
# 这里可以根据 GameManager 中的卡牌列表初始化牌组
if GameManager.card_list:
for card_id in GameManager.card_list:
# 每张卡牌加入多次(根据游戏需求调整)
for i in range(3): # 每张卡牌3份
deck.append(card_id)
# 洗牌
deck.shuffle()
print("Deck initialized with ", deck.size(), " cards")
# 发放初始手牌
func deal_initial_cards() -> void:
var cards_per_player = 5 # 每个玩家初始手牌数
for peer_id in players_data:
for i in range(cards_per_player):
if deck.size() > 0:
var card_id = deck.pop_front()
players_data[peer_id]["hand"].append(card_id)
broadcast_hands()
# RPC: 抽牌
@rpc("any_peer", "call_remote", "reliable")
func draw_card() -> void:
var peer_id = multiplayer.get_remote_sender_id()
# 检查是否是该玩家的回合
if peer_id != current_player_id:
return
# 检查牌组是否还有牌
if deck.size() == 0:
print("Deck is empty!")
return
var card_id = deck.pop_front()
players_data[peer_id]["hand"].append(card_id)
print("Player ", peer_id, " drew card: ", card_id)
broadcast_hands()
# RPC: 打出卡牌
@rpc("any_peer", "call_remote", "reliable")
func play_card(card_index: int, target_player_id: int = -1) -> void:
var peer_id = multiplayer.get_remote_sender_id()
# 检查是否是该玩家的回合
if peer_id != current_player_id:
return
# 检查卡牌索引是否有效
if card_index < 0 or card_index >= players_data[peer_id]["hand"].size():
return
var card_id = players_data[peer_id]["hand"][card_index]
# 移除手牌中的卡牌
players_data[peer_id]["hand"].remove_at(card_index)
# 执行卡牌效果
execute_card_effect(card_id, peer_id, target_player_id)
print("Player ", peer_id, " played card: ", card_id)
broadcast_hands()
broadcast_game_state()
# 执行卡牌效果
func execute_card_effect(card_id: int, player_id: int, target_id: int) -> void:
# 这里根据卡牌ID执行具体效果
# 示例:简单加分效果
if players_data.has(player_id):
players_data[player_id]["score"] += 10
# 可以添加更多复杂的卡牌效果逻辑
print("Card ", card_id, " effect executed by player ", player_id)
# RPC: 结束回合
@rpc("any_peer", "call_remote", "reliable")
func end_turn() -> void:
var peer_id = multiplayer.get_remote_sender_id()
if peer_id != current_player_id:
return
next_turn()
# 切换到下一个玩家
func next_turn() -> void:
var player_ids = players_data.keys()
var current_index = player_ids.find(current_player_id)
if current_index != -1:
current_index = (current_index + 1) % player_ids.size()
current_player_id = player_ids[current_index]
current_turn += 1
print("Turn ", current_turn, ". Current player: ", current_player_id)
broadcast_game_state()
# 检查所有玩家是否都准备好了
func check_all_players_ready() -> void:
var all_ready = true
for player_id in players_data:
if !players_data[player_id]["is_ready"]:
all_ready = false
break
if all_ready and players_data.size() >= 2:
print("All players ready! Starting game...")
start_game.rpc()
# 广播玩家列表
func broadcast_player_list() -> void:
update_players_data.rpc(players_data)
# RPC: 更新玩家数据
@rpc("call_remote", "reliable")
func update_players_data(data: Dictionary) -> void:
players_data = data
player_num = players_data.size()
emit_signal("players_updated", players_data)
# 广播游戏状态
func broadcast_game_state() -> void:
update_game_state.rpc(game_started, game_phase, current_turn, current_player_id)
# RPC: 更新游戏状态
@rpc("call_remote", "reliable")
func update_game_state(started: bool, phase: String, turn: int, current_id: int) -> void:
game_started = started
game_phase = phase
current_turn = turn
current_player_id = current_id
emit_signal("game_state_updated", game_started, game_phase, current_turn, current_player_id)
# 广播手牌信息(只发给对应玩家)
func broadcast_hands() -> void:
for peer_id in players_data:
send_hand.rpc_id(peer_id, players_data[peer_id]["hand"])
# 发送其他玩家的手牌数量
var hand_sizes = {}
for peer_id in players_data:
hand_sizes[peer_id] = players_data[peer_id]["hand"].size()
broadcast_hand_sizes.rpc(hand_sizes)
# RPC: 发送手牌
@rpc("call_remote", "reliable")
func send_hand(hand: Array) -> void:
var peer_id = multiplayer.get_unique_id()
if players_data.has(peer_id):
players_data[peer_id]["hand"] = hand
emit_signal("hand_updated", hand)
# RPC: 广播手牌数量
@rpc("call_remote", "reliable")
func broadcast_hand_sizes(sizes: Dictionary) -> void:
emit_signal("hand_sizes_updated", sizes)
# RPC: 结束游戏
@rpc("call_remote", "reliable")
func end_game() -> void:
game_started = false
game_phase = "ended"
broadcast_game_state()
# 计算并显示最终得分
var scores = {}
for player_id in players_data:
scores[player_id] = players_data[player_id]["score"]
emit_signal("game_ended", scores)
# 信号定义(需要在编辑器中连接或在其他脚本中连接)
signal players_updated(players: Dictionary)
signal game_state_updated(started: bool, phase: String, turn: int, current_player: int)
signal hand_updated(hand: Array)
signal hand_sizes_updated(sizes: Dictionary)
signal game_ended(scores: Dictionary)
+1
View File
@@ -0,0 +1 @@
uid://dkwc5qney4yee
+5
View File
@@ -0,0 +1,5 @@
# AI 生成代码文件
这个目录下的代码都是 AI 生成的,仅作参考。
不要将本目录下的代码直接复制到被使用的代码处。
+1 -1
View File
@@ -31,4 +31,4 @@ SETTINGS_TIP_LOADED,Tip: Finish Loading,提示:完成加载
SETTINGS_TIP_NOLOCALSOURCE,Tip: No local data or no selection,提示:无本地数据或未选择,无法加载 SETTINGS_TIP_NOLOCALSOURCE,Tip: No local data or no selection,提示:无本地数据或未选择,无法加载
GAMEUI_URNULL,Room Running: You are XXX XXXX,啊啊啊啊啊:啊啊啊啊 GAMEUI_URNULL,Room Running: You are XXX XXXX,啊啊啊啊啊:啊啊啊啊
GAMEUI_URHOST,Room Running: You are the Host,房间运行中:您是房主 GAMEUI_URHOST,Room Running: You are the Host,房间运行中:您是房主
GAMEUI_URGUEST,Room Running: You are a Guest,房间运行中:您是房客 GAMEUI_URGUEST,Room Running: You are a Guest,房间运行中:您是房客
1 keys en zh-cn
31 SETTINGS_TIP_NOLOCALSOURCE Tip: No local data or no selection 提示:无本地数据或未选择,无法加载
32 GAMEUI_URNULL Room Running: You are XXX XXXX 啊啊啊啊啊:啊啊啊啊
33 GAMEUI_URHOST Room Running: You are the Host 房间运行中:您是房主
34 GAMEUI_URGUEST Room Running: You are a Guest 房间运行中:您是房客
+1 -1
View File
@@ -21,10 +21,10 @@ config/icon="res://icon.svg"
[autoload] [autoload]
MultiGame="*res://scripts/autoload/MultiGame.gd"
SceneManager="*res://scripts/autoload/SceneManager.gd" SceneManager="*res://scripts/autoload/SceneManager.gd"
GameManager="*res://scripts/autoload/GameManager.gd" GameManager="*res://scripts/autoload/GameManager.gd"
DownloadManager="*res://scripts/autoload/DownloadManager.gd" DownloadManager="*res://scripts/autoload/DownloadManager.gd"
GameLoopManager="*uid://qxowgxfp4iwm"
[display] [display]
+4 -4
View File
@@ -7,10 +7,10 @@
script = ExtResource("1_yqjtg") script = ExtResource("1_yqjtg")
[node name="Background" type="ColorRect" parent="." unique_id=802635055] [node name="Background" type="ColorRect" parent="." unique_id=802635055]
offset_left = -34.0 offset_left = -29.0
offset_top = -27.0 offset_top = -18.0
offset_right = 2594.0 offset_right = 2599.0
offset_bottom = 1476.0 offset_bottom = 1485.0
color = Color(0.7058824, 0.6862745, 0, 1) color = Color(0.7058824, 0.6862745, 0, 1)
[node name="IsServerLabel" type="Label" parent="." unique_id=592205212] [node name="IsServerLabel" type="Label" parent="." unique_id=592205212]
+10 -10
View File
@@ -34,7 +34,7 @@ theme_override_fonts/font = ExtResource("2_6wm04")
theme_override_font_sizes/font_size = 24 theme_override_font_sizes/font_size = 24
[node name="IPBeginSetting" type="Node2D" parent="." unique_id=1428256194] [node name="IPBeginSetting" type="Node2D" parent="." unique_id=1428256194]
position = Vector2(-1, 94) position = Vector2(0, 100)
metadata/_edit_group_ = true metadata/_edit_group_ = true
[node name="IPBeginSetLabel" type="Label" parent="IPBeginSetting" unique_id=561890346] [node name="IPBeginSetLabel" type="Label" parent="IPBeginSetting" unique_id=561890346]
@@ -55,7 +55,7 @@ theme_override_fonts/font = ExtResource("2_6wm04")
theme_override_font_sizes/font_size = 24 theme_override_font_sizes/font_size = 24
[node name="UsernameSetting" type="Node2D" parent="." unique_id=564366013] [node name="UsernameSetting" type="Node2D" parent="." unique_id=564366013]
position = Vector2(0, 208) position = Vector2(0, 200)
metadata/_edit_group_ = true metadata/_edit_group_ = true
[node name="UsernameSetLabel" type="Label" parent="UsernameSetting" unique_id=1569658441] [node name="UsernameSetLabel" type="Label" parent="UsernameSetting" unique_id=1569658441]
@@ -76,7 +76,7 @@ theme_override_fonts/font = ExtResource("2_6wm04")
theme_override_font_sizes/font_size = 24 theme_override_font_sizes/font_size = 24
[node name="LoadSource" type="Node2D" parent="." unique_id=230611076] [node name="LoadSource" type="Node2D" parent="." unique_id=230611076]
position = Vector2(44, 314) position = Vector2(40, 300)
metadata/_edit_group_ = true metadata/_edit_group_ = true
[node name="LoadSourceLabel" type="Label" parent="LoadSource" unique_id=299353496] [node name="LoadSourceLabel" type="Label" parent="LoadSource" unique_id=299353496]
@@ -96,7 +96,7 @@ offset_bottom = 60.0
theme_override_font_sizes/font_size = 25 theme_override_font_sizes/font_size = 25
[node name="ChooseLanguage" type="Node2D" parent="." unique_id=323270128] [node name="ChooseLanguage" type="Node2D" parent="." unique_id=323270128]
position = Vector2(39, 405) position = Vector2(40, 400)
metadata/_edit_group_ = true metadata/_edit_group_ = true
[node name="ChooseLanguageLabel" type="Label" parent="ChooseLanguage" unique_id=1719434618] [node name="ChooseLanguageLabel" type="Label" parent="ChooseLanguage" unique_id=1719434618]
@@ -130,18 +130,18 @@ theme_override_font_sizes/font_size = 30
text = "SETTINGS_SAVESETTINGS" text = "SETTINGS_SAVESETTINGS"
[node name="DownloadButton" type="Button" parent="." unique_id=315804661] [node name="DownloadButton" type="Button" parent="." unique_id=315804661]
offset_left = 598.0 offset_left = 554.0
offset_top = 1316.0 offset_top = 1309.0
offset_right = 774.0 offset_right = 761.0
offset_bottom = 1405.0 offset_bottom = 1398.0
theme_override_fonts/font = ExtResource("2_6wm04") theme_override_fonts/font = ExtResource("2_6wm04")
theme_override_font_sizes/font_size = 30 theme_override_font_sizes/font_size = 30
text = "SETTINGS_DOWNLOAD" text = "SETTINGS_DOWNLOAD"
[node name="CancelButton" type="Button" parent="." unique_id=135993345] [node name="CancelButton" type="Button" parent="." unique_id=135993345]
offset_left = 371.0 offset_left = 347.0
offset_top = 1305.0 offset_top = 1305.0
offset_right = 550.0 offset_right = 581.0
offset_bottom = 1394.0 offset_bottom = 1394.0
theme_override_fonts/font = ExtResource("2_6wm04") theme_override_fonts/font = ExtResource("2_6wm04")
theme_override_font_sizes/font_size = 30 theme_override_font_sizes/font_size = 30
+28 -21
View File
@@ -3,32 +3,35 @@ extends Node
var uuid: String var uuid: String
func create_file(file_path: String) -> void: func create_file(file_path: String) -> void:
var base_dir = file_path.get_base_dir() var base_dir: String = file_path.get_base_dir()
if !DirAccess.dir_exists_absolute(base_dir): if !DirAccess.dir_exists_absolute(base_dir):
DirAccess.make_dir_recursive_absolute(base_dir) DirAccess.make_dir_recursive_absolute(base_dir)
var file = FileAccess.open(file_path, FileAccess.WRITE) var file: FileAccess = FileAccess.open(file_path, FileAccess.WRITE)
file.close() file.close()
func download_file(server_path: String, local_path: String) -> void: func download_file(server_path: String, local_path: String) -> void:
var http = HTTPRequest.new() var http: HTTPRequest = HTTPRequest.new()
add_child(http) add_child(http)
var file_path = local_path var file_path: String = local_path
if !FileAccess.file_exists(file_path): if !FileAccess.file_exists(file_path):
create_file(file_path) create_file(file_path)
http.download_file = file_path http.download_file = file_path
http.request(server_path) http.request(server_path)
await http.request_completed await http.request_completed
func get_uuid() -> void: func get_uuid() -> void:
var index_file = FileAccess.open("user://download/sources/temp/index.json", FileAccess.READ) var index_file: FileAccess = FileAccess.open("user://download/sources/temp/index.json", FileAccess.READ)
var index_text = index_file.get_as_text() var index_text: String = index_file.get_as_text()
var content = JSON.parse_string(index_text) var content = JSON.parse_string(index_text)
index_file.close() index_file.close()
uuid = content["uuid"] uuid = content["uuid"]
func download_defs(type: String, origin: String) -> void: func download_defs(type: String, origin: String) -> void:
var list_file = FileAccess.open("user://download/sources/%s/%ss/list.json" % [uuid, type], FileAccess.READ) var list_file: FileAccess = FileAccess.open("user://download/sources/%s/%ss/list.json" % [uuid, type], FileAccess.READ)
var list_text = list_file.get_as_text() var list_text: String = list_file.get_as_text()
var list = JSON.parse_string(list_text) var list = JSON.parse_string(list_text)
list_file.close() list_file.close()
if !list: if !list:
@@ -37,9 +40,10 @@ func download_defs(type: String, origin: String) -> void:
var filename = list[k] var filename = list[k]
await download_file("%s%s/id/%s" % [origin, type, k], "user://download/sources/%s/%ss/%s.json" % [uuid, type, filename]) await download_file("%s%s/id/%s" % [origin, type, k], "user://download/sources/%s/%ss/%s.json" % [uuid, type, filename])
func download_assets(origin: String) -> void: func download_assets(origin: String) -> void:
var list_file = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) var list_file: FileAccess = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ)
var list_text = list_file.get_as_text() var list_text: String = list_file.get_as_text()
var list = JSON.parse_string(list_text) var list = JSON.parse_string(list_text)
list_file.close() list_file.close()
if !list: if !list:
@@ -51,9 +55,10 @@ func download_assets(origin: String) -> void:
var filename = list["sounds"][k] var filename = list["sounds"][k]
await download_file("%sasset/sound/%s" % [origin, k], "user://download/sources/%s/assets/sounds/%s" % [uuid, filename]) await download_file("%sasset/sound/%s" % [origin, k], "user://download/sources/%s/assets/sounds/%s" % [uuid, filename])
func download_from_origin() -> int: func download_from_origin() -> int:
var origin = GameManager.data_origin var origin: String = GameManager.data_origin
var http = HTTPRequest.new() var http: HTTPRequest = HTTPRequest.new()
add_child(http) add_child(http)
if origin.substr(0, 4) != "http": if origin.substr(0, 4) != "http":
@@ -88,35 +93,37 @@ func download_from_origin() -> int:
return 0 return 0
func load_resource(): func load_resource():
var card_file = FileAccess.open("user://download/sources/%s/cards/list.json" % [uuid], FileAccess.READ) var card_file: FileAccess = FileAccess.open("user://download/sources/%s/cards/list.json" % [uuid], FileAccess.READ)
GameManager.card_list = JSON.parse_string(card_file.get_as_text()) GameManager.card_list = JSON.parse_string(card_file.get_as_text())
card_file.close() card_file.close()
var reaction_file = FileAccess.open("user://download/sources/%s/reactions/list.json" % [uuid], FileAccess.READ) var reaction_file: FileAccess = FileAccess.open("user://download/sources/%s/reactions/list.json" % [uuid], FileAccess.READ)
GameManager.reaction_list = JSON.parse_string(reaction_file.get_as_text()) GameManager.reaction_list = JSON.parse_string(reaction_file.get_as_text())
reaction_file.close() reaction_file.close()
var matter_file = FileAccess.open("user://download/sources/%s/matters/list.json" % [uuid], FileAccess.READ) var matter_file: FileAccess = FileAccess.open("user://download/sources/%s/matters/list.json" % [uuid], FileAccess.READ)
GameManager.matter_list = JSON.parse_string(matter_file.get_as_text()) GameManager.matter_list = JSON.parse_string(matter_file.get_as_text())
matter_file.close() matter_file.close()
var asset_file = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) var asset_file: FileAccess = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ)
var asset_list = JSON.parse_string(asset_file.get_as_text()) var asset_list: Dictionary = JSON.parse_string(asset_file.get_as_text())
GameManager.pic_list = asset_list["pics"] GameManager.pic_list = asset_list["pics"]
GameManager.sound_list = asset_list["sounds"] GameManager.sound_list = asset_list["sounds"]
asset_file.close() asset_file.close()
func get_sources(): func get_sources():
if !DirAccess.dir_exists_absolute("user://download/sources/"): if !DirAccess.dir_exists_absolute("user://download/sources/"):
DirAccess.make_dir_recursive_absolute("user://download/sources/") DirAccess.make_dir_recursive_absolute("user://download/sources/")
var dir = DirAccess.open("user://download/sources/") var dir: DirAccess = DirAccess.open("user://download/sources/")
var subdirs: PackedStringArray = dir.get_directories() var subdirs: PackedStringArray = dir.get_directories()
for subdir in subdirs: for subdir in subdirs:
if subdir == "temp": if subdir as String == "temp":
continue continue
var file = FileAccess.open("user://download/sources/%s/index.json" % [subdir], FileAccess.READ) var file: FileAccess = FileAccess.open("user://download/sources/%s/index.json" % [subdir], FileAccess.READ)
var text = file.get_as_text() var text: String = file.get_as_text()
var content = JSON.parse_string(text) var content = JSON.parse_string(text)
file.close() file.close()
var source_name = content["name"] var source_name = content["name"]
+45
View File
@@ -0,0 +1,45 @@
extends Node
var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new()
var max_player_num: int
var player_num: int
func create_server(playern: int) -> void:
max_player_num = playern
player_num = 1
if peer.create_server(GameManager.port, playern) != OK:
return
setup_multiplayer()
func create_client(ip: String) -> void:
if peer.create_client(ip, GameManager.port) != OK:
return
setup_multiplayer()
func setup_multiplayer() -> void:
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
func _on_peer_connected(id: int) -> void:
add_player(id)
func _on_peer_disconnected(id: int):
remove_player(id)
func add_player(id: int) -> void:
pass
func remove_player(id: int) -> void:
pass
func start_game() -> void:
pass
+1
View File
@@ -0,0 +1 @@
uid://qxowgxfp4iwm
+1
View File
@@ -2,6 +2,7 @@ extends Node
var data_origin: String = "" var data_origin: String = ""
var ip_begin: String = "192.168." var ip_begin: String = "192.168."
var port: int
var username: String = "Player1" var username: String = "Player1"
var source: int = -1 var source: int = -1
-128
View File
@@ -1,128 +0,0 @@
extends Node
var peer = ENetMultiplayerPeer.new()
var players: Array
var cards: Array
var my_card: Array
var max_players: int
var player_cards: Dictionary
var player_turns: Dictionary
var player_username: Dictionary
var player_hp: Dictionary
var server_round: int
var client_round: int
func add_player(id: int):
if players.size() < max_players:
players.append(id)
player_cards[id] = []
func remove_player(id: int):
for i in range(players.size()):
if players[i] == id:
players.pop_at(i)
player_cards.erase(id)
break
func create_server(playern: int) -> void:
max_players = playern
var error = peer.create_server(8989, playern)
if error != OK:
printerr(error)
return
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
func create_client(ip: String) -> void:
peer.create_client(ip, 8989)
multiplayer.multiplayer_peer = peer
func _on_peer_connected(id: int) -> void:
add_player(id)
func _on_peer_disconnected(id: int) -> void:
remove_player(id)
func start_game() -> void:
if players.size() != max_players:
return
deal_cards()
server_round = 1
remote_variable()
func extract() -> String:
if cards.size() == 0:
return ""
var index = randi() % cards.size()
var card = cards[index]
cards.pop_at(index)
return card
func deal_cards() -> void:
for player in players:
while player_cards[player].size() < 8:
var card = extract()
if card != "":
player_cards[player].append(card)
func next_round() -> void:
settle_round()
server_round += 1
func settle_round() -> void:
for player in players:
request_card_draw(player)
remote_variable()
func request_card_draw(player_id: int) -> void:
if server_round == 1 and 0 <= player_turns[player_id] <= 1:
for i in range(3):
if player_cards[player_id].size() >= 8:
break
var card = extract()
if card != "":
player_cards[player_id].append(card)
else:
for i in range(4):
if player_cards[player_id].size() >= 8:
break
var card = extract()
if card != "":
player_cards[player_id].append(card)
remote_variable()
func get_my_cards() -> void:
request_cards.rpc()
@rpc("any_peer", "call_remote", "reliable")
func request_cards() -> void:
var sender_id = multiplayer.get_remote_sender_id()
var data: Array = player_cards.get(sender_id, [-1])
send_cards.rpc_id(sender_id, data)
@rpc("authority", "call_remote", "reliable")
func send_cards(data: Array) -> void:
my_card = data
func remote_variable() -> void:
if not multiplayer.is_server():
return
var data: Dictionary = {}
data["cards"] = cards
data["player_cards"] = player_cards
data["player_turns"] = player_turns
data["player_hp"] = player_hp
data["server_round"] = server_round
get_remote_variable.rpc(data)
@rpc("authority", "call_remote", "reliable")
func get_remote_variable(data: Dictionary) -> void:
cards = data["cards"]
player_cards = data["player_cards"]
player_turns = data["player_turns"]
player_hp = data["player_hp"]
server_round = data["server_round"]
-1
View File
@@ -1 +0,0 @@
uid://dwbuaieufg51g
+7 -6
View File
@@ -2,13 +2,14 @@ extends Node
var current_scene = null var current_scene = null
var Card = ResourceLoader.load("res://prefabs/game/card.tscn") var Card: Resource = ResourceLoader.load("res://scenes/game/card.tscn")
func _ready(): func _ready():
var root = get_tree().root var root: Window = get_tree().root
current_scene = root.get_child(root.get_child_count() - 1) current_scene = root.get_child(root.get_child_count() - 1)
func goto_scene(path: String): func goto_scene(path: String):
get_tree().change_scene_to_file("res://scenes/%s.tscn" % [path]) get_tree().change_scene_to_file("res://scenes/%s.tscn" % [path])
var root = get_tree().root var root: Window = get_tree().root
current_scene = root.get_child(root.get_child_count() - 1) current_scene = root.get_child(root.get_child_count() - 1)
+5 -1
View File
@@ -4,17 +4,21 @@ var description: String
var type: String var type: String
var card_name: String var card_name: String
func set_texture(pic: String) -> void: func set_texture(pic: String) -> void:
var image = Image.new() var image: Image = Image.new()
image.load(pic) image.load(pic)
$Sprite.texture = ImageTexture.create_from_image(image) $Sprite.texture = ImageTexture.create_from_image(image)
func set_card(cname: String) -> void: func set_card(cname: String) -> void:
card_name = cname card_name = cname
set_texture("user://download/sources/%s/assets/pics/%s" % [DownloadManager.uuid, GameManager.pic_list[card_name]]) set_texture("user://download/sources/%s/assets/pics/%s" % [DownloadManager.uuid, GameManager.pic_list[card_name]])
func set_pos(x: int, y: int) -> void: func set_pos(x: int, y: int) -> void:
$Sprite.position = Vector2(x, y) $Sprite.position = Vector2(x, y)
func get_pos() -> Vector2: func get_pos() -> Vector2:
return $Sprite.position return $Sprite.position
+5 -4
View File
@@ -1,22 +1,22 @@
extends Node2D extends Node2D
var CardScene = preload("res://scenes/game/card.tscn")
var card_list: Array var card_list: Array
func _ready() -> void: func _ready() -> void:
init() init()
GameLoopManager.start_game()
var card = create_card("Oxygen") var card = create_card("Oxygen")
card.show() card.show()
card.set_pos(300, 300) card.set_pos(300, 300)
print(card.get_pos()) print(card.get_pos())
func init() -> void: func init() -> void:
# 设置 UI 展示文本
if multiplayer.is_server(): if multiplayer.is_server():
$IsServerLabel.text = "GAMEUI_URHOST" $IsServerLabel.text = "GAMEUI_URHOST"
else: else:
$IsServerLabel.text = "GAMEUI_URGUEST" $IsServerLabel.text = "GAMEUI_URGUEST"
var addresses: PackedStringArray = IP.get_local_addresses() var addresses: PackedStringArray = IP.get_local_addresses()
var ipaddress: String = "" var ipaddress: String = ""
for address in addresses: for address in addresses:
@@ -26,8 +26,9 @@ func init() -> void:
$IPLabel.text = ipaddress $IPLabel.text = ipaddress
$Player1/Username.text = GameManager.username $Player1/Username.text = GameManager.username
func create_card(card_name: String): func create_card(card_name: String):
var card = CardScene.instantiate() var card = SceneManager.Card.instantiate()
add_child(card) add_child(card)
card.set_card(card_name) card.set_card(card_name)
card_list.append(card) card_list.append(card)
+2 -2
View File
@@ -1,9 +1,9 @@
extends Node2D extends Node2D
func _on_create_game_button_pressed() -> void: func _on_create_game_button_pressed() -> void:
var player_num = int($CreateGameEdit.text) var player_num: int = int($CreateGameEdit.text)
if 2 <= player_num and player_num <= 4: if 2 <= player_num and player_num <= 4:
MultiGame.create_server(int(player_num)) GameLoopManager.create_server(int(player_num))
$".".hide() $".".hide()
SceneManager.goto_scene("game/game") SceneManager.goto_scene("game/game")
else: else:
+1 -1
View File
@@ -3,6 +3,6 @@ extends Node2D
func _on_join_game_button_pressed() -> void: func _on_join_game_button_pressed() -> void:
var ip: String = $JoinGameEdit.text var ip: String = $JoinGameEdit.text
MultiGame.create_client(ip) GameLoopManager.create_client(ip)
$".".hide() $".".hide()
SceneManager.goto_scene("game/game") SceneManager.goto_scene("game/game")
+3
View File
@@ -4,11 +4,14 @@ extends Node2D
func _on_join_game_pressed() -> void: func _on_join_game_pressed() -> void:
$JoinGameUI.show() $JoinGameUI.show()
func _on_start_game_pressed() -> void: func _on_start_game_pressed() -> void:
$CreateGameUI.show() $CreateGameUI.show()
func _on_setting_button_pressed() -> void: func _on_setting_button_pressed() -> void:
SceneManager.goto_scene("menus/settings") SceneManager.goto_scene("menus/settings")
func _on_quit_game_pressed() -> void: func _on_quit_game_pressed() -> void:
get_tree().quit() get_tree().quit()
+4 -1
View File
@@ -1,20 +1,24 @@
extends Node2D extends Node2D
func _ready() -> void: func _ready() -> void:
init_sources() init_sources()
init_text() init_text()
func init_text() -> void: func init_text() -> void:
$DataSetting/LineEdit.text = GameManager.data_origin $DataSetting/LineEdit.text = GameManager.data_origin
$IPBeginSetting/LineEdit.text = GameManager.ip_begin $IPBeginSetting/LineEdit.text = GameManager.ip_begin
$UsernameSetting/LineEdit.text = GameManager.username $UsernameSetting/LineEdit.text = GameManager.username
$LoadSource/ChooseSource.select(GameManager.source) $LoadSource/ChooseSource.select(GameManager.source)
func init_sources() -> void: func init_sources() -> void:
DownloadManager.get_sources() DownloadManager.get_sources()
for source_name in GameManager.sources: for source_name in GameManager.sources:
$LoadSource/ChooseSource.add_item(source_name) $LoadSource/ChooseSource.add_item(source_name)
func _on_save_button_pressed() -> void: func _on_save_button_pressed() -> void:
GameManager.data_origin = $DataSetting/LineEdit.text GameManager.data_origin = $DataSetting/LineEdit.text
GameManager.ip_begin = $IPBeginSetting/LineEdit.text GameManager.ip_begin = $IPBeginSetting/LineEdit.text
@@ -42,7 +46,6 @@ func _on_download_button_pressed() -> void:
$Tips.text = "SETTINGS_TIP_LOADED" $Tips.text = "SETTINGS_TIP_LOADED"
func _on_load_button_pressed() -> void: func _on_load_button_pressed() -> void:
if GameManager.sources.size() == 0 or GameManager.source == -1: if GameManager.sources.size() == 0 or GameManager.source == -1:
$Tips.text = "SETTINGS_TIP_NOLOCALSOURCE" $Tips.text = "SETTINGS_TIP_NOLOCALSOURCE"