From 4b45aa19a6a1180c3d43ad97befd268d55ccd889 Mon Sep 17 00:00:00 2001 From: Tiger Date: Thu, 2 Apr 2026 09:41:12 +0800 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=E8=AE=A9AI=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BA=86MultiGame.gd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/autoload/MultiGame.gd | 155 ++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 52 deletions(-) diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index a95ea23..8ab669d 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -1,128 +1,179 @@ extends Node -var peer = ENetMultiplayerPeer.new() +var peer: ENetMultiplayerPeer = 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 players: Array[int] = [] +var max_players: int = 0 +var player_cards: Dictionary = {} +var player_turns: Dictionary = {} +var player_username: Dictionary = {} +var player_hp: Dictionary = {} -var server_round: int -var client_round: int +var cards: Array[String] = [] +var my_card: Array[String] = [] -func add_player(id: int): - if players.size() < max_players: +var server_round: int = 0 +var client_round: int = 0 +var game_started: bool = false + +func add_player(id: int) -> void: + if players.size() < max_players and not players.has(id): players.append(id) player_cards[id] = [] + if not player_turns.has(id): + player_turns[id] = 0 + if not player_hp.has(id): + player_hp[id] = 100 -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 remove_player(id: int) -> void: + var index = players.find(id) + if index != -1: + players.remove_at(index) + player_cards.erase(id) + player_turns.erase(id) + player_hp.erase(id) + player_username.erase(id) func create_server(playern: int) -> void: max_players = playern var error = peer.create_server(8989, playern) if error != OK: - printerr(error) + push_error("Failed to create server: " + str(error)) return + multiplayer.multiplayer_peer = peer multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) + + add_player(1) func create_client(ip: String) -> void: - peer.create_client(ip, 8989) + var error = peer.create_client(ip, 8989) + if error != OK: + push_error("Failed to create client: " + str(error)) + return + multiplayer.multiplayer_peer = peer func _on_peer_connected(id: int) -> void: + print("Player connected: ", id) add_player(id) + sync_players.rpc() func _on_peer_disconnected(id: int) -> void: + print("Player disconnected: ", id) remove_player(id) + sync_players.rpc() func start_game() -> void: - if players.size() != max_players: + if not multiplayer.is_server(): + push_error("Only the server can start the game") return - deal_cards() + + if players.size() != max_players: + push_error("Not enough players to start the game") + return + + game_started = true server_round = 1 - remote_variable() + deal_cards() + sync_game_state.rpc() func extract() -> String: - if cards.size() == 0: + if cards.is_empty(): return "" + var index = randi() % cards.size() var card = cards[index] - cards.pop_at(index) + cards.remove_at(index) return card func deal_cards() -> void: - for player in players: - while player_cards[player].size() < 8: + for player_id in players: + while player_cards[player_id].size() < 8: var card = extract() if card != "": - player_cards[player].append(card) + player_cards[player_id].append(card) func next_round() -> void: + if not multiplayer.is_server(): + push_error("Only the server can advance rounds") + return + settle_round() server_round += 1 + sync_game_state.rpc() func settle_round() -> void: - for player in players: - request_card_draw(player) - remote_variable() + for player_id in players: + request_card_draw(player_id) 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() + if not player_cards.has(player_id): + push_error("Player not found: " + str(player_id)) + return + + var cards_to_draw: int = 4 + + if server_round == 1 and player_turns.has(player_id) and 0 <= player_turns[player_id] <= 1: + cards_to_draw = 3 + + for i in range(cards_to_draw): + if player_cards[player_id].size() >= 8: + break + var card = extract() + if card != "": + player_cards[player_id].append(card) func get_my_cards() -> void: - request_cards.rpc() + request_cards.rpc_id(1) @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]) + var data: Array = player_cards.get(sender_id, []) 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: +func sync_game_state() -> 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 + data["game_started"] = game_started - get_remote_variable.rpc(data) + sync_game_state_rpc.rpc(data) @rpc("authority", "call_remote", "reliable") -func get_remote_variable(data: Dictionary) -> void: +func sync_game_state_rpc(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"] + game_started = data["game_started"] + +func sync_players() -> void: + if not multiplayer.is_server(): + return + + var data: Dictionary = {} + data["players"] = players + data["player_username"] = player_username + data["player_hp"] = player_hp + + sync_players_rpc.rpc(data) + +@rpc("authority", "call_remote", "reliable") +func sync_players_rpc(data: Dictionary) -> void: + players = data["players"] + player_username = data["player_username"] + player_hp = data["player_hp"] From 62f2c998b7c35fe84d7ba7eb7e784e00ca1f2286 Mon Sep 17 00:00:00 2001 From: Tiger Date: Thu, 2 Apr 2026 15:35:12 +0800 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=E5=B0=86=E5=8D=A1=E7=89=8C?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=B8=AD=E7=9A=84=E5=9C=BA=E6=99=AF=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E4=B8=BASceneManager=E7=BB=9F=E4=B8=80=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/autoload/SceneManager.gd | 2 +- scripts/game/game.gd | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/autoload/SceneManager.gd b/scripts/autoload/SceneManager.gd index c9cbd52..4c3eaf0 100644 --- a/scripts/autoload/SceneManager.gd +++ b/scripts/autoload/SceneManager.gd @@ -2,7 +2,7 @@ extends Node var current_scene = null -var Card = ResourceLoader.load("res://prefabs/game/card.tscn") +var Card = ResourceLoader.load("res://scenes/game/card.tscn") func _ready(): var root = get_tree().root diff --git a/scripts/game/game.gd b/scripts/game/game.gd index c240936..8a6e549 100644 --- a/scripts/game/game.gd +++ b/scripts/game/game.gd @@ -1,7 +1,5 @@ extends Node2D -var CardScene = preload("res://scenes/game/card.tscn") - var card_list: Array func _ready() -> void: @@ -27,7 +25,7 @@ func init() -> void: $Player1/Username.text = GameManager.username func create_card(card_name: String): - var card = CardScene.instantiate() + var card = SceneManager.Card.instantiate() add_child(card) card.set_card(card_name) card_list.append(card) From 0f9297a9e1d1310d1ece8271aae34346df150da0 Mon Sep 17 00:00:00 2001 From: Tiger Date: Fri, 3 Apr 2026 13:04:50 +0800 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/game/game.tscn | 8 ++-- scenes/menus/settings.tscn | 16 +++---- scripts/autoload/MultiGame.gd | 85 +++++++++++++++-------------------- 3 files changed, 48 insertions(+), 61 deletions(-) diff --git a/scenes/game/game.tscn b/scenes/game/game.tscn index f1966ed..8feaa79 100644 --- a/scenes/game/game.tscn +++ b/scenes/game/game.tscn @@ -7,10 +7,10 @@ script = ExtResource("1_yqjtg") [node name="Background" type="ColorRect" parent="." unique_id=802635055] -offset_left = -34.0 -offset_top = -27.0 -offset_right = 2594.0 -offset_bottom = 1476.0 +offset_left = -29.0 +offset_top = -18.0 +offset_right = 2599.0 +offset_bottom = 1485.0 color = Color(0.7058824, 0.6862745, 0, 1) [node name="IsServerLabel" type="Label" parent="." unique_id=592205212] diff --git a/scenes/menus/settings.tscn b/scenes/menus/settings.tscn index 461323f..b331d71 100644 --- a/scenes/menus/settings.tscn +++ b/scenes/menus/settings.tscn @@ -34,7 +34,7 @@ theme_override_fonts/font = ExtResource("2_6wm04") theme_override_font_sizes/font_size = 24 [node name="IPBeginSetting" type="Node2D" parent="." unique_id=1428256194] -position = Vector2(-1, 94) +position = Vector2(0, 100) metadata/_edit_group_ = true [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 [node name="UsernameSetting" type="Node2D" parent="." unique_id=564366013] -position = Vector2(0, 208) +position = Vector2(0, 200) metadata/_edit_group_ = true [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 [node name="LoadSource" type="Node2D" parent="." unique_id=230611076] -position = Vector2(44, 314) +position = Vector2(40, 300) metadata/_edit_group_ = true [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 [node name="ChooseLanguage" type="Node2D" parent="." unique_id=323270128] -position = Vector2(39, 405) +position = Vector2(40, 400) metadata/_edit_group_ = true [node name="ChooseLanguageLabel" type="Label" parent="ChooseLanguage" unique_id=1719434618] @@ -130,10 +130,10 @@ theme_override_font_sizes/font_size = 30 text = "SETTINGS_SAVESETTINGS" [node name="DownloadButton" type="Button" parent="." unique_id=315804661] -offset_left = 598.0 -offset_top = 1316.0 -offset_right = 774.0 -offset_bottom = 1405.0 +offset_left = 595.0 +offset_top = 1299.0 +offset_right = 923.0 +offset_bottom = 1388.0 theme_override_fonts/font = ExtResource("2_6wm04") theme_override_font_sizes/font_size = 30 text = "SETTINGS_DOWNLOAD" diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index 8ab669d..bb045e6 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -17,22 +17,22 @@ var client_round: int = 0 var game_started: bool = false func add_player(id: int) -> void: - if players.size() < max_players and not players.has(id): - players.append(id) - player_cards[id] = [] - if not player_turns.has(id): - player_turns[id] = 0 - if not player_hp.has(id): - player_hp[id] = 100 + if players.size() >= max_players or players.has(id): + return + players.append(id) + player_cards[id] = [] + player_turns[id] = 0 + player_hp[id] = 100 func remove_player(id: int) -> void: var index = players.find(id) - if index != -1: - players.remove_at(index) - player_cards.erase(id) - player_turns.erase(id) - player_hp.erase(id) - player_username.erase(id) + if index == -1: + return + players.remove_at(index) + player_cards.erase(id) + player_turns.erase(id) + player_hp.erase(id) + player_username.erase(id) func create_server(playern: int) -> void: max_players = playern @@ -40,11 +40,9 @@ func create_server(playern: int) -> void: if error != OK: push_error("Failed to create server: " + str(error)) return - multiplayer.multiplayer_peer = peer multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) - add_player(1) func create_client(ip: String) -> void: @@ -52,7 +50,6 @@ func create_client(ip: String) -> void: if error != OK: push_error("Failed to create client: " + str(error)) return - multiplayer.multiplayer_peer = peer func _on_peer_connected(id: int) -> void: @@ -69,11 +66,9 @@ func start_game() -> void: if not multiplayer.is_server(): push_error("Only the server can start the game") return - if players.size() != max_players: push_error("Not enough players to start the game") return - game_started = true server_round = 1 deal_cards() @@ -82,7 +77,6 @@ func start_game() -> void: func extract() -> String: if cards.is_empty(): return "" - var index = randi() % cards.size() var card = cards[index] cards.remove_at(index) @@ -92,14 +86,14 @@ func deal_cards() -> void: for player_id in players: while player_cards[player_id].size() < 8: var card = extract() - if card != "": - player_cards[player_id].append(card) + if card.is_empty(): + break + player_cards[player_id].append(card) func next_round() -> void: if not multiplayer.is_server(): push_error("Only the server can advance rounds") return - settle_round() server_round += 1 sync_game_state.rpc() @@ -112,18 +106,16 @@ func request_card_draw(player_id: int) -> void: if not player_cards.has(player_id): push_error("Player not found: " + str(player_id)) return - - var cards_to_draw: int = 4 - - if server_round == 1 and player_turns.has(player_id) and 0 <= player_turns[player_id] <= 1: + var cards_to_draw := 4 + if server_round == 1 and player_turns.get(player_id, -1) <= 1: cards_to_draw = 3 - - for i in range(cards_to_draw): + for i in cards_to_draw: if player_cards[player_id].size() >= 8: break var card = extract() - if card != "": - player_cards[player_id].append(card) + if card.is_empty(): + break + player_cards[player_id].append(card) func get_my_cards() -> void: request_cards.rpc_id(1) @@ -131,8 +123,7 @@ func get_my_cards() -> void: @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, []) - send_cards.rpc_id(sender_id, data) + send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) @rpc("authority", "call_remote", "reliable") func send_cards(data: Array) -> void: @@ -141,16 +132,14 @@ func send_cards(data: Array) -> void: func sync_game_state() -> 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 - data["game_started"] = game_started - - sync_game_state_rpc.rpc(data) + sync_game_state_rpc.rpc({ + "cards": cards, + "player_cards": player_cards, + "player_turns": player_turns, + "player_hp": player_hp, + "server_round": server_round, + "game_started": game_started + }) @rpc("authority", "call_remote", "reliable") func sync_game_state_rpc(data: Dictionary) -> void: @@ -164,13 +153,11 @@ func sync_game_state_rpc(data: Dictionary) -> void: func sync_players() -> void: if not multiplayer.is_server(): return - - var data: Dictionary = {} - data["players"] = players - data["player_username"] = player_username - data["player_hp"] = player_hp - - sync_players_rpc.rpc(data) + sync_players_rpc.rpc({ + "players": players, + "player_username": player_username, + "player_hp": player_hp + }) @rpc("authority", "call_remote", "reliable") func sync_players_rpc(data: Dictionary) -> void: From fe80a3f38dc3e37016f8719e13b00df509938857 Mon Sep 17 00:00:00 2001 From: Tiger Date: Fri, 3 Apr 2026 20:59:12 +0800 Subject: [PATCH 04/15] =?UTF-8?q?refactor:=20AI=E9=87=8D=E6=9E=84MultiGame?= =?UTF-8?q?.gd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/autoload/MultiGame.gd | 113 ++++++++++++++++------------------ 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index bb045e6..4e18807 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -1,5 +1,11 @@ extends Node +const PORT: int = 8989 +const MAX_HAND_SIZE: int = 8 +const INITIAL_HP: int = 100 +const DEFAULT_DRAW_COUNT: int = 4 +const FIRST_ROUND_DRAW_COUNT: int = 3 + var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() var players: Array[int] = [] @@ -22,52 +28,42 @@ func add_player(id: int) -> void: players.append(id) player_cards[id] = [] player_turns[id] = 0 - player_hp[id] = 100 + player_hp[id] = INITIAL_HP func remove_player(id: int) -> void: - var index = players.find(id) - if index == -1: + if not players.has(id): return - players.remove_at(index) - player_cards.erase(id) - player_turns.erase(id) - player_hp.erase(id) - player_username.erase(id) + players.erase(id) + for dict in [player_cards, player_turns, player_hp, player_username]: + dict.erase(id) func create_server(playern: int) -> void: max_players = playern - var error = peer.create_server(8989, playern) - if error != OK: - push_error("Failed to create server: " + str(error)) + if peer.create_server(PORT, playern) != OK: return - multiplayer.multiplayer_peer = peer - multiplayer.peer_connected.connect(_on_peer_connected) - multiplayer.peer_disconnected.connect(_on_peer_disconnected) + _setup_multiplayer() add_player(1) func create_client(ip: String) -> void: - var error = peer.create_client(ip, 8989) - if error != OK: - push_error("Failed to create client: " + str(error)) + if peer.create_client(ip, 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: - print("Player connected: ", id) add_player(id) sync_players.rpc() func _on_peer_disconnected(id: int) -> void: - print("Player disconnected: ", id) remove_player(id) sync_players.rpc() func start_game() -> void: - if not multiplayer.is_server(): - push_error("Only the server can start the game") - return - if players.size() != max_players: - push_error("Not enough players to start the game") + if not multiplayer.is_server() or players.size() != max_players: return game_started = true server_round = 1 @@ -77,22 +73,17 @@ func start_game() -> void: func extract() -> String: if cards.is_empty(): return "" - var index = randi() % cards.size() - var card = cards[index] + var index := randi() % cards.size() + var card := cards[index] cards.remove_at(index) return card func deal_cards() -> void: for player_id in players: - while player_cards[player_id].size() < 8: - var card = extract() - if card.is_empty(): - break - player_cards[player_id].append(card) + _draw_cards(player_id, MAX_HAND_SIZE) func next_round() -> void: if not multiplayer.is_server(): - push_error("Only the server can advance rounds") return settle_round() server_round += 1 @@ -100,29 +91,29 @@ func next_round() -> void: func settle_round() -> void: for player_id in players: - request_card_draw(player_id) + var draw_count := DEFAULT_DRAW_COUNT + if server_round == 1 and player_turns.get(player_id, -1) <= 1: + draw_count = FIRST_ROUND_DRAW_COUNT + _draw_cards(player_id, draw_count) -func request_card_draw(player_id: int) -> void: +func _draw_cards(player_id: int, count: int) -> void: if not player_cards.has(player_id): - push_error("Player not found: " + str(player_id)) return - var cards_to_draw := 4 - if server_round == 1 and player_turns.get(player_id, -1) <= 1: - cards_to_draw = 3 - for i in cards_to_draw: - if player_cards[player_id].size() >= 8: + var hand: Array = player_cards[player_id] + for i in count: + if hand.size() >= MAX_HAND_SIZE: break - var card = extract() + var card := extract() if card.is_empty(): break - player_cards[player_id].append(card) + hand.append(card) func get_my_cards() -> void: request_cards.rpc_id(1) @rpc("any_peer", "call_remote", "reliable") func request_cards() -> void: - var sender_id = multiplayer.get_remote_sender_id() + var sender_id := multiplayer.get_remote_sender_id() send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) @rpc("authority", "call_remote", "reliable") @@ -133,34 +124,34 @@ func sync_game_state() -> void: if not multiplayer.is_server(): return sync_game_state_rpc.rpc({ - "cards": cards, - "player_cards": player_cards, - "player_turns": player_turns, - "player_hp": player_hp, - "server_round": server_round, - "game_started": game_started + cards = cards, + player_cards = player_cards, + player_turns = player_turns, + player_hp = player_hp, + server_round = server_round, + game_started = game_started }) @rpc("authority", "call_remote", "reliable") func sync_game_state_rpc(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"] - game_started = data["game_started"] + cards = data.cards + player_cards = data.player_cards + player_turns = data.player_turns + player_hp = data.player_hp + server_round = data.server_round + game_started = data.game_started func sync_players() -> void: if not multiplayer.is_server(): return sync_players_rpc.rpc({ - "players": players, - "player_username": player_username, - "player_hp": player_hp + players = players, + player_username = player_username, + player_hp = player_hp }) @rpc("authority", "call_remote", "reliable") func sync_players_rpc(data: Dictionary) -> void: - players = data["players"] - player_username = data["player_username"] - player_hp = data["player_hp"] + players = data.players + player_username = data.player_username + player_hp = data.player_hp From c0c92fb2c4fbf4682d8684d156e84e029c7a53d4 Mon Sep 17 00:00:00 2001 From: Tiger Date: Sat, 4 Apr 2026 13:52:31 +0800 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2103c97..36211b9 100644 --- a/README.md +++ b/README.md @@ -14,32 +14,32 @@ game/ - project.godot - icon.svg - assets/ - - fonts/ - - AlibabaPuHuiTi-3-65-Medium.ttf # 阿里巴巴普惠体 - - translation/ - - trans.csv + - fonts/ + - AlibabaPuHuiTi-3-65-Medium.ttf # 阿里巴巴普惠体 + - translation/ + - trans.csv - scenes/ - - menus/ - - main_menu.tscn - - settings.tscn - - game/ - - game.tscn - - card.tscn + - menus/ + - main_menu.tscn + - settings.tscn + - game/ + - game.tscn + - card.tscn - scripts/ - - autoload/ - - GameManager.gd # 游戏管理 - - DownloadManager.gd # 下载管理 - - MultiGame.gd # 多人游戏功能 - - SceneManager.gd # 场景管理 - - main_menu/ - - main_menu.gd - - join_game_ui.gd - - create_game_ui.gd - - game/ - - game.gd - - card.gd - - settings/ - - settings.gd + - autoload/ + - GameManager.gd # 游戏管理 + - DownloadManager.gd # 下载管理 + - MultiGame.gd # 多人游戏功能 + - SceneManager.gd # 场景管理 + - main_menu/ + - main_menu.gd + - join_game_ui.gd + - create_game_ui.gd + - game/ + - game.gd + - card.gd + - settings/ + - settings.gd ``` ## 如何运行 @@ -56,4 +56,4 @@ game/ - 除连接了信号或 HTTPRequest、MultiplayerAPI 的函数外,任何函数都不应该以下划线(`_`)开头。 -- 函数之间只需间隔一行,无需间隔两行。 +- 函数之间既可间隔一行,也可间隔两行。建议间隔两行,部分间隔一行的函数为遗留问题。 From 61ce331a00b0550e51857ef8d7de59baa8e4141b Mon Sep 17 00:00:00 2001 From: Tiger Date: Sun, 5 Apr 2026 14:31:16 +0800 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=E4=BA=86?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=BE=AA=E7=8E=AF=E7=AE=A1=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++++- project.godot | 1 + scripts/autoload/GameLoopManager.gd | 22 ++++++++++++++++++++++ scripts/autoload/GameLoopManager.gd.uid | 1 + scripts/autoload/MultiGame.gd | 3 +-- scripts/game/game.gd | 4 +++- 6 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 scripts/autoload/GameLoopManager.gd create mode 100644 scripts/autoload/GameLoopManager.gd.uid diff --git a/README.md b/README.md index 36211b9..7a136db 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ game/ - DownloadManager.gd # 下载管理 - MultiGame.gd # 多人游戏功能 - SceneManager.gd # 场景管理 + - GameLoopManager.gd # 游戏循环管理 - main_menu/ - main_menu.gd - join_game_ui.gd @@ -46,6 +47,12 @@ game/ 请先运行数据后端,在游戏设置中输入后端 URL(包含端口号和 `http://` 或 `https://` 前缀),或者选择本地已有的数据源,然后创建游戏或加入游戏开始游玩。 +## 部分游戏逻辑介绍 + +### 游戏循环 + +游戏循环管理器(GameLoopManager.gd)负责游戏游戏循环的管理,其中不可避免地与多人游戏管理器(MultiGame.gd)交互与交叉。 + ## 最佳实践 本项目目前正在使用 Godot 4.6.2 进行开发。开发用语言为 GDScript。 @@ -56,4 +63,4 @@ game/ - 除连接了信号或 HTTPRequest、MultiplayerAPI 的函数外,任何函数都不应该以下划线(`_`)开头。 -- 函数之间既可间隔一行,也可间隔两行。建议间隔两行,部分间隔一行的函数为遗留问题。 +- 函数之间既可间隔一行,也可间隔两行。建议绑定 Godot 信号的函数间隔两行(自动空行),其余函数间隔一行,部分间隔一行的函数为遗留问题。 diff --git a/project.godot b/project.godot index 4f3512f..0bbb471 100644 --- a/project.godot +++ b/project.godot @@ -25,6 +25,7 @@ MultiGame="*res://scripts/autoload/MultiGame.gd" SceneManager="*res://scripts/autoload/SceneManager.gd" GameManager="*res://scripts/autoload/GameManager.gd" DownloadManager="*res://scripts/autoload/DownloadManager.gd" +GameLoopManager="*uid://qxowgxfp4iwm" [display] diff --git a/scripts/autoload/GameLoopManager.gd b/scripts/autoload/GameLoopManager.gd new file mode 100644 index 0000000..24a837c --- /dev/null +++ b/scripts/autoload/GameLoopManager.gd @@ -0,0 +1,22 @@ +extends Node + +var game_round: int = -1 + +signal end_game + +func _ready() -> void: + end_game.connect(_on_end_game) + +func start_game() -> void: + game_round = 1 + +func settle_round() -> void: + game_round += 1 + if check_game_end(): + end_game.emit() + +func check_game_end() -> bool: + return false + +func _on_end_game() -> void: + game_round = -1 diff --git a/scripts/autoload/GameLoopManager.gd.uid b/scripts/autoload/GameLoopManager.gd.uid new file mode 100644 index 0000000..5d90182 --- /dev/null +++ b/scripts/autoload/GameLoopManager.gd.uid @@ -0,0 +1 @@ +uid://qxowgxfp4iwm diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index 4e18807..cc6b44a 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -2,7 +2,7 @@ extends Node const PORT: int = 8989 const MAX_HAND_SIZE: int = 8 -const INITIAL_HP: int = 100 +const INITIAL_HP: int = 4 const DEFAULT_DRAW_COUNT: int = 4 const FIRST_ROUND_DRAW_COUNT: int = 3 @@ -19,7 +19,6 @@ var cards: Array[String] = [] var my_card: Array[String] = [] var server_round: int = 0 -var client_round: int = 0 var game_started: bool = false func add_player(id: int) -> void: diff --git a/scripts/game/game.gd b/scripts/game/game.gd index 8a6e549..a3291ed 100644 --- a/scripts/game/game.gd +++ b/scripts/game/game.gd @@ -4,17 +4,18 @@ var card_list: Array func _ready() -> void: init() + GameLoopManager.start_game() var card = create_card("Oxygen") card.show() card.set_pos(300, 300) print(card.get_pos()) func init() -> void: + # 设置 UI 展示文本 if multiplayer.is_server(): $IsServerLabel.text = "GAMEUI_URHOST" else: $IsServerLabel.text = "GAMEUI_URGUEST" - var addresses: PackedStringArray = IP.get_local_addresses() var ipaddress: String = "" for address in addresses: @@ -24,6 +25,7 @@ func init() -> void: $IPLabel.text = ipaddress $Player1/Username.text = GameManager.username + func create_card(card_name: String): var card = SceneManager.Card.instantiate() add_child(card) From 0118bf613cde5634020d01e98f4f9c8bc09014cd Mon Sep 17 00:00:00 2001 From: Tiger Date: Wed, 15 Apr 2026 16:42:48 +0800 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translation/trans.csv | 2 +- scenes/menus/settings.tscn | 12 ++++++------ scripts/autoload/MultiGame.gd | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/translation/trans.csv b/assets/translation/trans.csv index 9b1905a..c6ce3d5 100644 --- a/assets/translation/trans.csv +++ b/assets/translation/trans.csv @@ -31,4 +31,4 @@ SETTINGS_TIP_LOADED,Tip: Finish Loading,提示:完成加载 SETTINGS_TIP_NOLOCALSOURCE,Tip: No local data or no selection,提示:无本地数据或未选择,无法加载 GAMEUI_URNULL,Room Running: You are XXX XXXX,啊啊啊啊啊:啊啊啊啊 GAMEUI_URHOST,Room Running: You are the Host,房间运行中:您是房主 -GAMEUI_URGUEST,Room Running: You are a Guest,房间运行中:您是房客 \ No newline at end of file +GAMEUI_URGUEST,Room Running: You are a Guest,房间运行中:您是房客 diff --git a/scenes/menus/settings.tscn b/scenes/menus/settings.tscn index b331d71..ae08286 100644 --- a/scenes/menus/settings.tscn +++ b/scenes/menus/settings.tscn @@ -130,18 +130,18 @@ theme_override_font_sizes/font_size = 30 text = "SETTINGS_SAVESETTINGS" [node name="DownloadButton" type="Button" parent="." unique_id=315804661] -offset_left = 595.0 -offset_top = 1299.0 -offset_right = 923.0 -offset_bottom = 1388.0 +offset_left = 554.0 +offset_top = 1309.0 +offset_right = 761.0 +offset_bottom = 1398.0 theme_override_fonts/font = ExtResource("2_6wm04") theme_override_font_sizes/font_size = 30 text = "SETTINGS_DOWNLOAD" [node name="CancelButton" type="Button" parent="." unique_id=135993345] -offset_left = 371.0 +offset_left = 347.0 offset_top = 1305.0 -offset_right = 550.0 +offset_right = 581.0 offset_bottom = 1394.0 theme_override_fonts/font = ExtResource("2_6wm04") theme_override_font_sizes/font_size = 30 diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index cc6b44a..6aa3669 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -1,6 +1,6 @@ extends Node -const PORT: int = 8989 +var PORT: int = 8989 const MAX_HAND_SIZE: int = 8 const INITIAL_HP: int = 4 const DEFAULT_DRAW_COUNT: int = 4 From 2cad85257e27dde87f74a23faa05f88a2ec98a44 Mon Sep 17 00:00:00 2001 From: Tiger Date: Sat, 25 Apr 2026 12:12:28 +0800 Subject: [PATCH 08/15] =?UTF-8?q?dev:=20=E4=BB=A3=E7=A0=81=E5=BC=BA?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- scripts/autoload/DownloadManager.gd | 40 ++++++++++++++--------------- scripts/autoload/SceneManager.gd | 10 ++++---- scripts/game/card.gd | 2 +- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index e001aa4..d2cfed0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .godot/ -.vscode/ \ No newline at end of file +.vscode/ +.idea/ \ No newline at end of file diff --git a/scripts/autoload/DownloadManager.gd b/scripts/autoload/DownloadManager.gd index 2bc2689..72109c7 100644 --- a/scripts/autoload/DownloadManager.gd +++ b/scripts/autoload/DownloadManager.gd @@ -3,16 +3,16 @@ extends Node var uuid: String 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): 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() func download_file(server_path: String, local_path: String) -> void: - var http = HTTPRequest.new() + var http: HTTPRequest = HTTPRequest.new() add_child(http) - var file_path = local_path + var file_path: String = local_path if !FileAccess.file_exists(file_path): create_file(file_path) http.download_file = file_path @@ -20,15 +20,15 @@ func download_file(server_path: String, local_path: String) -> void: await http.request_completed func get_uuid() -> void: - var index_file = FileAccess.open("user://download/sources/temp/index.json", FileAccess.READ) - var index_text = index_file.get_as_text() + var index_file: FileAccess = FileAccess.open("user://download/sources/temp/index.json", FileAccess.READ) + var index_text: String = index_file.get_as_text() var content = JSON.parse_string(index_text) index_file.close() uuid = content["uuid"] 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_text = list_file.get_as_text() + var list_file: FileAccess = FileAccess.open("user://download/sources/%s/%ss/list.json" % [uuid, type], FileAccess.READ) + var list_text: String = list_file.get_as_text() var list = JSON.parse_string(list_text) list_file.close() if !list: @@ -38,8 +38,8 @@ func download_defs(type: String, origin: String) -> void: 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: - var list_file = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) - var list_text = list_file.get_as_text() + var list_file: FileAccess = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) + var list_text: String = list_file.get_as_text() var list = JSON.parse_string(list_text) list_file.close() if !list: @@ -52,8 +52,8 @@ func download_assets(origin: String) -> void: await download_file("%sasset/sound/%s" % [origin, k], "user://download/sources/%s/assets/sounds/%s" % [uuid, filename]) func download_from_origin() -> int: - var origin = GameManager.data_origin - var http = HTTPRequest.new() + var origin: String = GameManager.data_origin + var http: HTTPRequest = HTTPRequest.new() add_child(http) if origin.substr(0, 4) != "http": @@ -89,20 +89,20 @@ func download_from_origin() -> int: return 0 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()) 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()) 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()) matter_file.close() - var asset_file = 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_file: FileAccess = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) + var asset_list: Dictionary = JSON.parse_string(asset_file.get_as_text()) GameManager.pic_list = asset_list["pics"] GameManager.sound_list = asset_list["sounds"] asset_file.close() @@ -110,13 +110,13 @@ func load_resource(): func get_sources(): if !DirAccess.dir_exists_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() for subdir in subdirs: if subdir == "temp": continue - var file = FileAccess.open("user://download/sources/%s/index.json" % [subdir], FileAccess.READ) - var text = file.get_as_text() + var file: FileAccess = FileAccess.open("user://download/sources/%s/index.json" % [subdir], FileAccess.READ) + var text: String = file.get_as_text() var content = JSON.parse_string(text) file.close() var source_name = content["name"] diff --git a/scripts/autoload/SceneManager.gd b/scripts/autoload/SceneManager.gd index 4c3eaf0..431338d 100644 --- a/scripts/autoload/SceneManager.gd +++ b/scripts/autoload/SceneManager.gd @@ -5,10 +5,10 @@ var current_scene = null var Card = ResourceLoader.load("res://scenes/game/card.tscn") func _ready(): - var root = get_tree().root - current_scene = root.get_child(root.get_child_count() - 1) + var root: Window = get_tree().root + current_scene = root.get_child(root.get_child_count() - 1) func goto_scene(path: String): - get_tree().change_scene_to_file("res://scenes/%s.tscn" % [path]) - var root = get_tree().root - current_scene = root.get_child(root.get_child_count() - 1) \ No newline at end of file + get_tree().change_scene_to_file("res://scenes/%s.tscn" % [path]) + var root: Window = get_tree().root + current_scene = root.get_child(root.get_child_count() - 1) \ No newline at end of file diff --git a/scripts/game/card.gd b/scripts/game/card.gd index 5f20fb4..f40f042 100644 --- a/scripts/game/card.gd +++ b/scripts/game/card.gd @@ -5,7 +5,7 @@ var type: String var card_name: String func set_texture(pic: String) -> void: - var image = Image.new() + var image: Image = Image.new() image.load(pic) $Sprite.texture = ImageTexture.create_from_image(image) From 4834d7b33ba6e8d469124cfcb378e45a156cf0b4 Mon Sep 17 00:00:00 2001 From: Tiger Date: Sat, 25 Apr 2026 21:31:39 +0800 Subject: [PATCH 09/15] =?UTF-8?q?dev:=20=E5=87=BD=E6=95=B0=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E9=97=B4=E9=9A=94=E4=B8=A4=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- scripts/autoload/DownloadManager.gd | 7 +++++++ scripts/autoload/GameLoopManager.gd | 4 ++++ scripts/autoload/MultiGame.gd | 19 +++++++++++++++++++ scripts/autoload/SceneManager.gd | 1 + scripts/game/card.gd | 4 ++++ scripts/game/game.gd | 1 + scripts/main_menu/main_menu.gd | 3 +++ scripts/settings/settings.gd | 5 ++++- 9 files changed, 44 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a136db..a5cb2c8 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,4 @@ game/ - 除连接了信号或 HTTPRequest、MultiplayerAPI 的函数外,任何函数都不应该以下划线(`_`)开头。 -- 函数之间既可间隔一行,也可间隔两行。建议绑定 Godot 信号的函数间隔两行(自动空行),其余函数间隔一行,部分间隔一行的函数为遗留问题。 +- 函数之间既可间隔一行,也可间隔两行。建议函数间隔两行,部分间隔一行的函数为遗留问题。 diff --git a/scripts/autoload/DownloadManager.gd b/scripts/autoload/DownloadManager.gd index 72109c7..61c35f4 100644 --- a/scripts/autoload/DownloadManager.gd +++ b/scripts/autoload/DownloadManager.gd @@ -9,6 +9,7 @@ func create_file(file_path: String) -> void: var file: FileAccess = FileAccess.open(file_path, FileAccess.WRITE) file.close() + func download_file(server_path: String, local_path: String) -> void: var http: HTTPRequest = HTTPRequest.new() add_child(http) @@ -19,6 +20,7 @@ func download_file(server_path: String, local_path: String) -> void: http.request(server_path) await http.request_completed + func get_uuid() -> void: var index_file: FileAccess = FileAccess.open("user://download/sources/temp/index.json", FileAccess.READ) var index_text: String = index_file.get_as_text() @@ -26,6 +28,7 @@ func get_uuid() -> void: index_file.close() uuid = content["uuid"] + func download_defs(type: String, origin: String) -> void: var list_file: FileAccess = FileAccess.open("user://download/sources/%s/%ss/list.json" % [uuid, type], FileAccess.READ) var list_text: String = list_file.get_as_text() @@ -37,6 +40,7 @@ func download_defs(type: String, origin: String) -> void: var filename = list[k] 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: var list_file: FileAccess = FileAccess.open("user://download/sources/%s/assets/list.json" % [uuid], FileAccess.READ) var list_text: String = list_file.get_as_text() @@ -51,6 +55,7 @@ func download_assets(origin: String) -> void: var filename = list["sounds"][k] await download_file("%sasset/sound/%s" % [origin, k], "user://download/sources/%s/assets/sounds/%s" % [uuid, filename]) + func download_from_origin() -> int: var origin: String = GameManager.data_origin var http: HTTPRequest = HTTPRequest.new() @@ -88,6 +93,7 @@ func download_from_origin() -> int: return 0 + func load_resource(): 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()) @@ -107,6 +113,7 @@ func load_resource(): GameManager.sound_list = asset_list["sounds"] asset_file.close() + func get_sources(): if !DirAccess.dir_exists_absolute("user://download/sources/"): DirAccess.make_dir_recursive_absolute("user://download/sources/") diff --git a/scripts/autoload/GameLoopManager.gd b/scripts/autoload/GameLoopManager.gd index 24a837c..16a3d76 100644 --- a/scripts/autoload/GameLoopManager.gd +++ b/scripts/autoload/GameLoopManager.gd @@ -7,16 +7,20 @@ signal end_game func _ready() -> void: end_game.connect(_on_end_game) + func start_game() -> void: game_round = 1 + func settle_round() -> void: game_round += 1 if check_game_end(): end_game.emit() + func check_game_end() -> bool: return false + func _on_end_game() -> void: game_round = -1 diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd index 6aa3669..516b2d0 100644 --- a/scripts/autoload/MultiGame.gd +++ b/scripts/autoload/MultiGame.gd @@ -29,6 +29,7 @@ func add_player(id: int) -> void: player_turns[id] = 0 player_hp[id] = INITIAL_HP + func remove_player(id: int) -> void: if not players.has(id): return @@ -36,6 +37,7 @@ func remove_player(id: int) -> void: for dict in [player_cards, player_turns, player_hp, player_username]: dict.erase(id) + func create_server(playern: int) -> void: max_players = playern if peer.create_server(PORT, playern) != OK: @@ -43,24 +45,29 @@ func create_server(playern: int) -> void: _setup_multiplayer() add_player(1) + func create_client(ip: String) -> void: if peer.create_client(ip, 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) sync_players.rpc() + func _on_peer_disconnected(id: int) -> void: remove_player(id) sync_players.rpc() + func start_game() -> void: if not multiplayer.is_server() or players.size() != max_players: return @@ -69,6 +76,7 @@ func start_game() -> void: deal_cards() sync_game_state.rpc() + func extract() -> String: if cards.is_empty(): return "" @@ -77,10 +85,12 @@ func extract() -> String: cards.remove_at(index) return card + func deal_cards() -> void: for player_id in players: _draw_cards(player_id, MAX_HAND_SIZE) + func next_round() -> void: if not multiplayer.is_server(): return @@ -88,6 +98,7 @@ func next_round() -> void: server_round += 1 sync_game_state.rpc() + func settle_round() -> void: for player_id in players: var draw_count := DEFAULT_DRAW_COUNT @@ -95,6 +106,7 @@ func settle_round() -> void: draw_count = FIRST_ROUND_DRAW_COUNT _draw_cards(player_id, draw_count) + func _draw_cards(player_id: int, count: int) -> void: if not player_cards.has(player_id): return @@ -107,18 +119,22 @@ func _draw_cards(player_id: int, count: int) -> void: break hand.append(card) + func get_my_cards() -> void: request_cards.rpc_id(1) + @rpc("any_peer", "call_remote", "reliable") func request_cards() -> void: var sender_id := multiplayer.get_remote_sender_id() send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) + @rpc("authority", "call_remote", "reliable") func send_cards(data: Array) -> void: my_card = data + func sync_game_state() -> void: if not multiplayer.is_server(): return @@ -131,6 +147,7 @@ func sync_game_state() -> void: game_started = game_started }) + @rpc("authority", "call_remote", "reliable") func sync_game_state_rpc(data: Dictionary) -> void: cards = data.cards @@ -140,6 +157,7 @@ func sync_game_state_rpc(data: Dictionary) -> void: server_round = data.server_round game_started = data.game_started + func sync_players() -> void: if not multiplayer.is_server(): return @@ -149,6 +167,7 @@ func sync_players() -> void: player_hp = player_hp }) + @rpc("authority", "call_remote", "reliable") func sync_players_rpc(data: Dictionary) -> void: players = data.players diff --git a/scripts/autoload/SceneManager.gd b/scripts/autoload/SceneManager.gd index 431338d..f04d894 100644 --- a/scripts/autoload/SceneManager.gd +++ b/scripts/autoload/SceneManager.gd @@ -8,6 +8,7 @@ func _ready(): var root: Window = get_tree().root current_scene = root.get_child(root.get_child_count() - 1) + func goto_scene(path: String): get_tree().change_scene_to_file("res://scenes/%s.tscn" % [path]) var root: Window = get_tree().root diff --git a/scripts/game/card.gd b/scripts/game/card.gd index f40f042..88fcd4a 100644 --- a/scripts/game/card.gd +++ b/scripts/game/card.gd @@ -4,17 +4,21 @@ var description: String var type: String var card_name: String + func set_texture(pic: String) -> void: var image: Image = Image.new() image.load(pic) $Sprite.texture = ImageTexture.create_from_image(image) + func set_card(cname: String) -> void: card_name = cname set_texture("user://download/sources/%s/assets/pics/%s" % [DownloadManager.uuid, GameManager.pic_list[card_name]]) + func set_pos(x: int, y: int) -> void: $Sprite.position = Vector2(x, y) + func get_pos() -> Vector2: return $Sprite.position diff --git a/scripts/game/game.gd b/scripts/game/game.gd index a3291ed..f9d2e94 100644 --- a/scripts/game/game.gd +++ b/scripts/game/game.gd @@ -10,6 +10,7 @@ func _ready() -> void: card.set_pos(300, 300) print(card.get_pos()) + func init() -> void: # 设置 UI 展示文本 if multiplayer.is_server(): diff --git a/scripts/main_menu/main_menu.gd b/scripts/main_menu/main_menu.gd index 8978e02..1ba7bf1 100644 --- a/scripts/main_menu/main_menu.gd +++ b/scripts/main_menu/main_menu.gd @@ -4,11 +4,14 @@ extends Node2D func _on_join_game_pressed() -> void: $JoinGameUI.show() + func _on_start_game_pressed() -> void: $CreateGameUI.show() + func _on_setting_button_pressed() -> void: SceneManager.goto_scene("menus/settings") + func _on_quit_game_pressed() -> void: get_tree().quit() diff --git a/scripts/settings/settings.gd b/scripts/settings/settings.gd index b1c7b0e..9f92fe9 100644 --- a/scripts/settings/settings.gd +++ b/scripts/settings/settings.gd @@ -1,20 +1,24 @@ extends Node2D + func _ready() -> void: init_sources() init_text() + func init_text() -> void: $DataSetting/LineEdit.text = GameManager.data_origin $IPBeginSetting/LineEdit.text = GameManager.ip_begin $UsernameSetting/LineEdit.text = GameManager.username $LoadSource/ChooseSource.select(GameManager.source) + func init_sources() -> void: DownloadManager.get_sources() for source_name in GameManager.sources: $LoadSource/ChooseSource.add_item(source_name) + func _on_save_button_pressed() -> void: GameManager.data_origin = $DataSetting/LineEdit.text GameManager.ip_begin = $IPBeginSetting/LineEdit.text @@ -42,7 +46,6 @@ func _on_download_button_pressed() -> void: $Tips.text = "SETTINGS_TIP_LOADED" - func _on_load_button_pressed() -> void: if GameManager.sources.size() == 0 or GameManager.source == -1: $Tips.text = "SETTINGS_TIP_NOLOCALSOURCE" From c183ca49e2b24cb9037a460476dcd36a86bca9d3 Mon Sep 17 00:00:00 2001 From: Tiger Date: Sun, 26 Apr 2026 13:33:14 +0800 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20=E5=B8=B8=E8=A7=84=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/autoload/DownloadManager.gd | 2 +- scripts/autoload/SceneManager.gd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/autoload/DownloadManager.gd b/scripts/autoload/DownloadManager.gd index 61c35f4..b848630 100644 --- a/scripts/autoload/DownloadManager.gd +++ b/scripts/autoload/DownloadManager.gd @@ -120,7 +120,7 @@ func get_sources(): var dir: DirAccess = DirAccess.open("user://download/sources/") var subdirs: PackedStringArray = dir.get_directories() for subdir in subdirs: - if subdir == "temp": + if subdir as String == "temp": continue var file: FileAccess = FileAccess.open("user://download/sources/%s/index.json" % [subdir], FileAccess.READ) var text: String = file.get_as_text() diff --git a/scripts/autoload/SceneManager.gd b/scripts/autoload/SceneManager.gd index f04d894..6c03cab 100644 --- a/scripts/autoload/SceneManager.gd +++ b/scripts/autoload/SceneManager.gd @@ -2,7 +2,7 @@ extends Node var current_scene = null -var Card = ResourceLoader.load("res://scenes/game/card.tscn") +var Card: Resource = ResourceLoader.load("res://scenes/game/card.tscn") func _ready(): var root: Window = get_tree().root From da88be55675374837550c63e843d8a585dcd871b Mon Sep 17 00:00:00 2001 From: Tiger Date: Mon, 27 Apr 2026 14:07:53 +0800 Subject: [PATCH 11/15] =?UTF-8?q?feat:=20=E5=90=88=E5=B9=B6MultiGame.gd?= =?UTF-8?q?=E5=92=8CGameLoopManager.gd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project.godot | 1 - scripts/autoload/GameLoopManager.gd | 188 +++++++++++++++++++++++++--- scripts/autoload/MultiGame.gd | 175 -------------------------- scripts/autoload/MultiGame.gd.uid | 1 - scripts/main_menu/create_game_ui.gd | 4 +- scripts/main_menu/join_game_ui.gd | 2 +- 6 files changed, 176 insertions(+), 195 deletions(-) delete mode 100644 scripts/autoload/MultiGame.gd delete mode 100644 scripts/autoload/MultiGame.gd.uid diff --git a/project.godot b/project.godot index 0bbb471..7c02f1f 100644 --- a/project.godot +++ b/project.godot @@ -21,7 +21,6 @@ config/icon="res://icon.svg" [autoload] -MultiGame="*res://scripts/autoload/MultiGame.gd" SceneManager="*res://scripts/autoload/SceneManager.gd" GameManager="*res://scripts/autoload/GameManager.gd" DownloadManager="*res://scripts/autoload/DownloadManager.gd" diff --git a/scripts/autoload/GameLoopManager.gd b/scripts/autoload/GameLoopManager.gd index 16a3d76..89c26ab 100644 --- a/scripts/autoload/GameLoopManager.gd +++ b/scripts/autoload/GameLoopManager.gd @@ -1,26 +1,184 @@ extends Node -var game_round: int = -1 +var PORT: int = 8989 +const MAX_HAND_SIZE: int = 8 +const INITIAL_HP: int = 4 +const DEFAULT_DRAW_COUNT: int = 4 +const FIRST_ROUND_DRAW_COUNT: int = 3 -signal end_game +var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() + +var players: Array[int] = [] +var max_players: int = 0 +var player_cards: Dictionary = {} +var player_turns: Dictionary = {} +var player_username: Dictionary = {} +var player_hp: Dictionary = {} + +var cards: Array[String] = [] +var my_card: Array[String] = [] + +var server_round: int = 0 +var game_started: bool = false + +signal game_end func _ready() -> void: - end_game.connect(_on_end_game) + game_end.connect(_on_end_game) + +func add_player(id: int) -> void: + if players.size() >= max_players or players.has(id): + return + players.append(id) + player_cards[id] = [] + player_turns[id] = 0 + player_hp[id] = INITIAL_HP + + +func remove_player(id: int) -> void: + if not players.has(id): + return + players.erase(id) + for dict in [player_cards, player_turns, player_hp, player_username]: + dict.erase(id) + + +func create_server(playern: int) -> void: + max_players = playern + if peer.create_server(PORT, playern) != OK: + return + _setup_multiplayer() + add_player(1) + + +func create_client(ip: String) -> void: + if peer.create_client(ip, 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) + sync_players.rpc() + + +func _on_peer_disconnected(id: int) -> void: + remove_player(id) + sync_players.rpc() func start_game() -> void: - game_round = 1 - - -func settle_round() -> void: - game_round += 1 - if check_game_end(): - end_game.emit() - - -func check_game_end() -> bool: - return false + if not multiplayer.is_server() or players.size() != max_players: + return + game_started = true + server_round = 1 + deal_cards() + sync_game_state.rpc() func _on_end_game() -> void: - game_round = -1 + pass + + +func extract() -> String: + if cards.is_empty(): + return "" + var index := randi() % cards.size() + var card := cards[index] + cards.remove_at(index) + return card + + +func deal_cards() -> void: + for player_id in players: + _draw_cards(player_id, MAX_HAND_SIZE) + + +func next_round() -> void: + if not multiplayer.is_server(): + return + settle_round() + server_round += 1 + sync_game_state.rpc() + + +func settle_round() -> void: + for player_id in players: + var draw_count := DEFAULT_DRAW_COUNT + if server_round == 1 and player_turns.get(player_id, -1) <= 1: + draw_count = FIRST_ROUND_DRAW_COUNT + _draw_cards(player_id, draw_count) + + +func _draw_cards(player_id: int, count: int) -> void: + if not player_cards.has(player_id): + return + var hand: Array = player_cards[player_id] + for i in count: + if hand.size() >= MAX_HAND_SIZE: + break + var card := extract() + if card.is_empty(): + break + hand.append(card) + + +func get_my_cards() -> void: + request_cards.rpc_id(1) + + +@rpc("any_peer", "call_remote", "reliable") +func request_cards() -> void: + var sender_id := multiplayer.get_remote_sender_id() + send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) + + +@rpc("authority", "call_remote", "reliable") +func send_cards(data: Array) -> void: + my_card = data + + +func sync_game_state() -> void: + if not multiplayer.is_server(): + return + sync_game_state_rpc.rpc({ + cards = cards, + player_cards = player_cards, + player_turns = player_turns, + player_hp = player_hp, + server_round = server_round, + game_started = game_started + }) + + +@rpc("authority", "call_remote", "reliable") +func sync_game_state_rpc(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 + game_started = data.game_started + + +func sync_players() -> void: + if not multiplayer.is_server(): + return + sync_players_rpc.rpc({ + players = players, + player_username = player_username, + player_hp = player_hp + }) + + +@rpc("authority", "call_remote", "reliable") +func sync_players_rpc(data: Dictionary) -> void: + players = data.players + player_username = data.player_username + player_hp = data.player_hp diff --git a/scripts/autoload/MultiGame.gd b/scripts/autoload/MultiGame.gd deleted file mode 100644 index 516b2d0..0000000 --- a/scripts/autoload/MultiGame.gd +++ /dev/null @@ -1,175 +0,0 @@ -extends Node - -var PORT: int = 8989 -const MAX_HAND_SIZE: int = 8 -const INITIAL_HP: int = 4 -const DEFAULT_DRAW_COUNT: int = 4 -const FIRST_ROUND_DRAW_COUNT: int = 3 - -var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() - -var players: Array[int] = [] -var max_players: int = 0 -var player_cards: Dictionary = {} -var player_turns: Dictionary = {} -var player_username: Dictionary = {} -var player_hp: Dictionary = {} - -var cards: Array[String] = [] -var my_card: Array[String] = [] - -var server_round: int = 0 -var game_started: bool = false - -func add_player(id: int) -> void: - if players.size() >= max_players or players.has(id): - return - players.append(id) - player_cards[id] = [] - player_turns[id] = 0 - player_hp[id] = INITIAL_HP - - -func remove_player(id: int) -> void: - if not players.has(id): - return - players.erase(id) - for dict in [player_cards, player_turns, player_hp, player_username]: - dict.erase(id) - - -func create_server(playern: int) -> void: - max_players = playern - if peer.create_server(PORT, playern) != OK: - return - _setup_multiplayer() - add_player(1) - - -func create_client(ip: String) -> void: - if peer.create_client(ip, 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) - sync_players.rpc() - - -func _on_peer_disconnected(id: int) -> void: - remove_player(id) - sync_players.rpc() - - -func start_game() -> void: - if not multiplayer.is_server() or players.size() != max_players: - return - game_started = true - server_round = 1 - deal_cards() - sync_game_state.rpc() - - -func extract() -> String: - if cards.is_empty(): - return "" - var index := randi() % cards.size() - var card := cards[index] - cards.remove_at(index) - return card - - -func deal_cards() -> void: - for player_id in players: - _draw_cards(player_id, MAX_HAND_SIZE) - - -func next_round() -> void: - if not multiplayer.is_server(): - return - settle_round() - server_round += 1 - sync_game_state.rpc() - - -func settle_round() -> void: - for player_id in players: - var draw_count := DEFAULT_DRAW_COUNT - if server_round == 1 and player_turns.get(player_id, -1) <= 1: - draw_count = FIRST_ROUND_DRAW_COUNT - _draw_cards(player_id, draw_count) - - -func _draw_cards(player_id: int, count: int) -> void: - if not player_cards.has(player_id): - return - var hand: Array = player_cards[player_id] - for i in count: - if hand.size() >= MAX_HAND_SIZE: - break - var card := extract() - if card.is_empty(): - break - hand.append(card) - - -func get_my_cards() -> void: - request_cards.rpc_id(1) - - -@rpc("any_peer", "call_remote", "reliable") -func request_cards() -> void: - var sender_id := multiplayer.get_remote_sender_id() - send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) - - -@rpc("authority", "call_remote", "reliable") -func send_cards(data: Array) -> void: - my_card = data - - -func sync_game_state() -> void: - if not multiplayer.is_server(): - return - sync_game_state_rpc.rpc({ - cards = cards, - player_cards = player_cards, - player_turns = player_turns, - player_hp = player_hp, - server_round = server_round, - game_started = game_started - }) - - -@rpc("authority", "call_remote", "reliable") -func sync_game_state_rpc(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 - game_started = data.game_started - - -func sync_players() -> void: - if not multiplayer.is_server(): - return - sync_players_rpc.rpc({ - players = players, - player_username = player_username, - player_hp = player_hp - }) - - -@rpc("authority", "call_remote", "reliable") -func sync_players_rpc(data: Dictionary) -> void: - players = data.players - player_username = data.player_username - player_hp = data.player_hp diff --git a/scripts/autoload/MultiGame.gd.uid b/scripts/autoload/MultiGame.gd.uid deleted file mode 100644 index 2a42f7c..0000000 --- a/scripts/autoload/MultiGame.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dwbuaieufg51g diff --git a/scripts/main_menu/create_game_ui.gd b/scripts/main_menu/create_game_ui.gd index e56477b..aeec451 100644 --- a/scripts/main_menu/create_game_ui.gd +++ b/scripts/main_menu/create_game_ui.gd @@ -1,9 +1,9 @@ extends Node2D 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: - MultiGame.create_server(int(player_num)) + GameLoopManager.create_server(int(player_num)) $".".hide() SceneManager.goto_scene("game/game") else: diff --git a/scripts/main_menu/join_game_ui.gd b/scripts/main_menu/join_game_ui.gd index 7b78a49..efdc016 100644 --- a/scripts/main_menu/join_game_ui.gd +++ b/scripts/main_menu/join_game_ui.gd @@ -3,6 +3,6 @@ extends Node2D func _on_join_game_button_pressed() -> void: var ip: String = $JoinGameEdit.text - MultiGame.create_client(ip) + GameLoopManager.create_client(ip) $".".hide() SceneManager.goto_scene("game/game") From 85b473a27043bbf4cbd8af229c024f48de67924b Mon Sep 17 00:00:00 2001 From: Tiger Date: Mon, 27 Apr 2026 17:38:45 +0800 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=E5=BC=80=E5=A7=8B=E9=87=8D?= =?UTF-8?q?=E5=86=99GameLoopManager.gd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/autoload/GameLoopManager.gd | 171 +++------------------------- scripts/autoload/GameManager.gd | 1 + 2 files changed, 15 insertions(+), 157 deletions(-) diff --git a/scripts/autoload/GameLoopManager.gd b/scripts/autoload/GameLoopManager.gd index 89c26ab..54ef861 100644 --- a/scripts/autoload/GameLoopManager.gd +++ b/scripts/autoload/GameLoopManager.gd @@ -1,63 +1,25 @@ extends Node -var PORT: int = 8989 -const MAX_HAND_SIZE: int = 8 -const INITIAL_HP: int = 4 -const DEFAULT_DRAW_COUNT: int = 4 -const FIRST_ROUND_DRAW_COUNT: int = 3 - var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() -var players: Array[int] = [] -var max_players: int = 0 -var player_cards: Dictionary = {} -var player_turns: Dictionary = {} -var player_username: Dictionary = {} -var player_hp: Dictionary = {} - -var cards: Array[String] = [] -var my_card: Array[String] = [] - -var server_round: int = 0 -var game_started: bool = false - -signal game_end - -func _ready() -> void: - game_end.connect(_on_end_game) - -func add_player(id: int) -> void: - if players.size() >= max_players or players.has(id): - return - players.append(id) - player_cards[id] = [] - player_turns[id] = 0 - player_hp[id] = INITIAL_HP - - -func remove_player(id: int) -> void: - if not players.has(id): - return - players.erase(id) - for dict in [player_cards, player_turns, player_hp, player_username]: - dict.erase(id) - +var max_player_num: int +var player_num: int func create_server(playern: int) -> void: - max_players = playern - if peer.create_server(PORT, playern) != OK: + max_player_num = playern + player_num = 1 + if peer.create_server(GameManager.port, playern) != OK: return - _setup_multiplayer() - add_player(1) - + setup_multiplayer() + func create_client(ip: String) -> void: - if peer.create_client(ip, PORT) != OK: + if peer.create_client(ip, GameManager.port) != OK: return - _setup_multiplayer() + setup_multiplayer() -func _setup_multiplayer() -> void: +func setup_multiplayer() -> void: multiplayer.multiplayer_peer = peer multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) @@ -65,120 +27,15 @@ func _setup_multiplayer() -> void: func _on_peer_connected(id: int) -> void: add_player(id) - sync_players.rpc() -func _on_peer_disconnected(id: int) -> void: +func _on_peer_disconnected(id: int): remove_player(id) - sync_players.rpc() -func start_game() -> void: - if not multiplayer.is_server() or players.size() != max_players: - return - game_started = true - server_round = 1 - deal_cards() - sync_game_state.rpc() - - -func _on_end_game() -> void: +func add_player(id: int) -> void: pass -func extract() -> String: - if cards.is_empty(): - return "" - var index := randi() % cards.size() - var card := cards[index] - cards.remove_at(index) - return card - - -func deal_cards() -> void: - for player_id in players: - _draw_cards(player_id, MAX_HAND_SIZE) - - -func next_round() -> void: - if not multiplayer.is_server(): - return - settle_round() - server_round += 1 - sync_game_state.rpc() - - -func settle_round() -> void: - for player_id in players: - var draw_count := DEFAULT_DRAW_COUNT - if server_round == 1 and player_turns.get(player_id, -1) <= 1: - draw_count = FIRST_ROUND_DRAW_COUNT - _draw_cards(player_id, draw_count) - - -func _draw_cards(player_id: int, count: int) -> void: - if not player_cards.has(player_id): - return - var hand: Array = player_cards[player_id] - for i in count: - if hand.size() >= MAX_HAND_SIZE: - break - var card := extract() - if card.is_empty(): - break - hand.append(card) - - -func get_my_cards() -> void: - request_cards.rpc_id(1) - - -@rpc("any_peer", "call_remote", "reliable") -func request_cards() -> void: - var sender_id := multiplayer.get_remote_sender_id() - send_cards.rpc_id(sender_id, player_cards.get(sender_id, [])) - - -@rpc("authority", "call_remote", "reliable") -func send_cards(data: Array) -> void: - my_card = data - - -func sync_game_state() -> void: - if not multiplayer.is_server(): - return - sync_game_state_rpc.rpc({ - cards = cards, - player_cards = player_cards, - player_turns = player_turns, - player_hp = player_hp, - server_round = server_round, - game_started = game_started - }) - - -@rpc("authority", "call_remote", "reliable") -func sync_game_state_rpc(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 - game_started = data.game_started - - -func sync_players() -> void: - if not multiplayer.is_server(): - return - sync_players_rpc.rpc({ - players = players, - player_username = player_username, - player_hp = player_hp - }) - - -@rpc("authority", "call_remote", "reliable") -func sync_players_rpc(data: Dictionary) -> void: - players = data.players - player_username = data.player_username - player_hp = data.player_hp +func remove_player(id: int) -> void: + pass diff --git a/scripts/autoload/GameManager.gd b/scripts/autoload/GameManager.gd index 2bb2e44..05db204 100644 --- a/scripts/autoload/GameManager.gd +++ b/scripts/autoload/GameManager.gd @@ -2,6 +2,7 @@ extends Node var data_origin: String = "" var ip_begin: String = "192.168." +var port: int var username: String = "Player1" var source: int = -1 From 6868f7b4c99cea0f47a9f7a282836e4ce0860fd9 Mon Sep 17 00:00:00 2001 From: Tiger Date: Mon, 4 May 2026 11:10:56 +0800 Subject: [PATCH 13/15] =?UTF-8?q?feat:=20=E4=B8=8A=E4=BC=A0AI=E7=94=9F?= =?UTF-8?q?=E6=88=90=E4=BB=A3=E7=A0=81=E4=BE=9B=E5=8F=82=E8=80=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai-summon/GameLoopManager.gd | 317 +++++++++++++++++++++++++++++++++++ ai-summon/README.md | 3 + 2 files changed, 320 insertions(+) create mode 100644 ai-summon/GameLoopManager.gd create mode 100644 ai-summon/README.md diff --git a/ai-summon/GameLoopManager.gd b/ai-summon/GameLoopManager.gd new file mode 100644 index 0000000..aad794c --- /dev/null +++ b/ai-summon/GameLoopManager.gd @@ -0,0 +1,317 @@ +extends Node + +var peer: ENetMultiplayerPeer = ENetMultiplayer.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) \ No newline at end of file diff --git a/ai-summon/README.md b/ai-summon/README.md new file mode 100644 index 0000000..8619693 --- /dev/null +++ b/ai-summon/README.md @@ -0,0 +1,3 @@ +# AI 生成代码文件 + +这个目录下的代码都是 AI 生成的,仅作参考。 \ No newline at end of file From ae684f1c8f75455c0856ca95f3724e43878e10e6 Mon Sep 17 00:00:00 2001 From: Tiger Date: Thu, 14 May 2026 12:38:28 +0800 Subject: [PATCH 14/15] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +-- ai-summon/GameLoopManager.gd.uid | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 ai-summon/GameLoopManager.gd.uid diff --git a/README.md b/README.md index a5cb2c8..bdeeff3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ game/ - autoload/ - GameManager.gd # 游戏管理 - DownloadManager.gd # 下载管理 - - MultiGame.gd # 多人游戏功能 - SceneManager.gd # 场景管理 - GameLoopManager.gd # 游戏循环管理 - main_menu/ @@ -51,7 +50,7 @@ game/ ### 游戏循环 -游戏循环管理器(GameLoopManager.gd)负责游戏游戏循环的管理,其中不可避免地与多人游戏管理器(MultiGame.gd)交互与交叉。 +游戏循环管理器(GameLoopManager.gd)负责游戏游戏循环的管理。 ## 最佳实践 diff --git a/ai-summon/GameLoopManager.gd.uid b/ai-summon/GameLoopManager.gd.uid new file mode 100644 index 0000000..c92d0e7 --- /dev/null +++ b/ai-summon/GameLoopManager.gd.uid @@ -0,0 +1 @@ +uid://dkwc5qney4yee From 92c5e16f924a4e3ba99758185c11b6daf17c4234 Mon Sep 17 00:00:00 2001 From: Tiger Date: Mon, 18 May 2026 09:51:28 +0800 Subject: [PATCH 15/15] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=8F=AF=E8=83=BD=E5=AF=BC=E8=87=B4=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=B4=A9=E6=BA=83=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai-summon/GameLoopManager.gd | 2 +- ai-summon/README.md | 4 +++- scripts/autoload/GameLoopManager.gd | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ai-summon/GameLoopManager.gd b/ai-summon/GameLoopManager.gd index aad794c..06d227b 100644 --- a/ai-summon/GameLoopManager.gd +++ b/ai-summon/GameLoopManager.gd @@ -1,6 +1,6 @@ extends Node -var peer: ENetMultiplayerPeer = ENetMultiplayer.new() +var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() var max_player_num: int var player_num: int diff --git a/ai-summon/README.md b/ai-summon/README.md index 8619693..5a71200 100644 --- a/ai-summon/README.md +++ b/ai-summon/README.md @@ -1,3 +1,5 @@ # AI 生成代码文件 -这个目录下的代码都是 AI 生成的,仅作参考。 \ No newline at end of file +这个目录下的代码都是 AI 生成的,仅作参考。 + +不要将本目录下的代码直接复制到被使用的代码处。 \ No newline at end of file diff --git a/scripts/autoload/GameLoopManager.gd b/scripts/autoload/GameLoopManager.gd index 54ef861..efc6d2b 100644 --- a/scripts/autoload/GameLoopManager.gd +++ b/scripts/autoload/GameLoopManager.gd @@ -39,3 +39,7 @@ func add_player(id: int) -> void: func remove_player(id: int) -> void: pass + + +func start_game() -> void: + pass