fix: resolve 43 GDScript warnings and fix card display

- Rename shadowed variables (round->_round, seed->_seed, is_processing->_is_processing)
- Prefix unused parameters with underscore throughout
- Add @warning_ignore for false positive integer_division warnings
- Fix unused_signal warnings in event_bus.gd
- Fix CardNode display: move setup() after add_child() so @onready works
- Redesign CardNode with Panel background, suit symbols, red/black colors
- Delete unused _current_hint in TrainingController
This commit is contained in:
xiaji
2026-05-30 22:38:52 +08:00
parent bad46b0109
commit cef2cba7a5
14 changed files with 577 additions and 512 deletions

View File

@@ -4,7 +4,7 @@ extends RefCounted
var ai_name: String = "AI"
func decide(hand: Array, table: Array, current_rank: int, config: RuleConfig) -> HandEvaluator.EvaluatedPlay:
func decide(_hand: Array, _table: Array, _current_rank: int, _config: RuleConfig) -> HandEvaluator.EvaluatedPlay:
var pass_play := HandEvaluator.EvaluatedPlay.new()
pass_play.type = -1
pass_play.primary_rank = 0

View File

@@ -31,7 +31,7 @@ func _score_all(moves: Array, hand_size: int, current_rank: int) -> Array:
results.append({"move": m, "score": score})
return results
func _score_move(play: HandEvaluator.EvaluatedPlay, hand_size: int, current_rank: int) -> float:
func _score_move(play: HandEvaluator.EvaluatedPlay, hand_size: int, _current_rank: int) -> float:
var score := 0.0
var remaining := hand_size - play.cards.size()
score += (27.0 - remaining) / 27.0 * 0.3

View File

@@ -25,9 +25,9 @@ class Team:
var score: int = 0
var current_level: int = 2
static func create_team(team_id: int, p1: int, p2: int) -> Team:
static func create_team(p_team_id: int, p1: int, p2: int) -> Team:
var t := Team.new()
t.team_id = team_id
t.team_id = p_team_id
t.player_indices = [p1, p2]
return t

View File

@@ -16,9 +16,10 @@ static func _rank_for(original_id: int) -> int:
return 15
if original_id == 53:
return 16
@warning_ignore("integer_division")
return 2 + (original_id / 4)
static func create(seed: int = -1) -> Deck:
static func create(p_seed: int = -1) -> Deck:
var d := Deck.new()
d._cards = []
for deck_idx in range(2):
@@ -29,15 +30,15 @@ static func create(seed: int = -1) -> Deck:
var c := Card.create(orig_id, suit, rank)
c.card_id = global_id
d._cards.append(c)
if seed >= 0:
d._shuffle_with_seed(seed)
if p_seed >= 0:
d._shuffle_with_seed(p_seed)
else:
d._shuffle_random()
return d
func _shuffle_with_seed(seed: int) -> void:
func _shuffle_with_seed(p_seed: int) -> void:
var rng := RandomNumberGenerator.new()
rng.seed = seed
rng.seed = p_seed
for i in range(_cards.size() - 1, 0, -1):
var j := rng.randi_range(0, i)
var tmp := _cards[i]

View File

@@ -14,9 +14,9 @@ var teams: Array = []
var player_hands: Array = [[], [], [], []]
var player_names: Array[String] = ["Player", "AI-1", "AI-2", "AI-3"]
var player_human: Array[bool] = [true, false, false, false]
var round: Round
var _round: Round
var action_log: Array = []
var seed: int = 0
var _seed: int = 0
var finished_players: Array[int] = []
var current_winner_team: int = -1
var game_end_reason: String = ""
@@ -25,11 +25,11 @@ static func create(config: RuleConfig, seed_: int = -1) -> GameState:
var gs := GameState.new()
gs.rule_config = config
if seed_ >= 0:
gs.seed = seed_
gs._seed = seed_
else:
gs.seed = Time.get_unix_time_from_system() as int
gs._seed = Time.get_unix_time_from_system() as int
gs.teams = [_Actions.Team.create_team(0, 0, 2), _Actions.Team.create_team(1, 1, 3)]
gs.round = Round.new()
gs._round = Round.new()
return gs
func get_team(player_idx: int) -> Actions.Team:
@@ -91,7 +91,7 @@ func to_packed_snapshot(for_player: int) -> Dictionary:
"phase": phase,
"current_rank": current_rank,
"own_hand": _pack_hand(player_hands[for_player]),
"table": _pack_table(round.table),
"table": _pack_table(_round.table),
"finished": finished_players.duplicate(),
"team_scores": [teams[0].score, teams[1].score]
}

View File

@@ -74,7 +74,7 @@ static func _eval_three(reals: Array[Card], wilds: Array[Card], has_wild: bool)
return _make_result(_C.TYPE_TRIPLE, reals[0].rank(), false, reals + wilds.slice(0, 2))
return null
static func _eval_four(reals: Array[Card], wilds: Array[Card], has_wild: bool, current_rank: int, config: RuleConfig) -> EvaluatedPlay:
static func _eval_four(reals: Array[Card], wilds: Array[Card], has_wild: bool, _current_rank: int, _config: RuleConfig) -> EvaluatedPlay:
var all_cards := reals + wilds
if not has_wild and reals.size() == 4:
if _all_same_rank(reals):
@@ -94,7 +94,7 @@ static func _eval_four(reals: Array[Card], wilds: Array[Card], has_wild: bool, c
return _make_result(_C.TYPE_BOMB, reals[0].rank(), false, all_cards)
return null
static func _eval_multi(reals: Array[Card], wilds: Array[Card], has_wild: bool, current_rank: int, n: int, config: RuleConfig) -> EvaluatedPlay:
static func _eval_multi(reals: Array[Card], wilds: Array[Card], has_wild: bool, _current_rank: int, n: int, config: RuleConfig) -> EvaluatedPlay:
if not has_wild:
return _eval_pure_multiple(reals, n, config)
if n == 5:
@@ -103,9 +103,11 @@ static func _eval_multi(reals: Array[Card], wilds: Array[Card], has_wild: bool,
result = _eval_straight(reals, wilds, 5, config)
if result != null: return result
if n >= 6 and n % 2 == 0:
@warning_ignore("integer_division")
var result := _eval_consecutive_pairs(reals, wilds, n / 2, config)
if result != null: return result
if n >= 6 and n % 3 == 0:
@warning_ignore("integer_division")
var result := _eval_steel_plate(reals, wilds, n / 3, config)
if result != null: return result
if n >= 5:
@@ -116,6 +118,7 @@ static func _eval_multi(reals: Array[Card], wilds: Array[Card], has_wild: bool,
static func _eval_pure_multiple(reals: Array[Card], n: int, config: RuleConfig) -> EvaluatedPlay:
if n >= 5 and _is_straight(reals, config):
return _make_result(_C.TYPE_STRAIGHT, reals[n-1].rank(), true, reals)
@warning_ignore("integer_division")
if n >= 6 and n % 2 == 0 and _is_consecutive_pairs(reals, n / 2):
return _make_result(_C.TYPE_CONSECUTIVE_PAIRS, reals[n-1].rank(), true, reals)
if n == 5 and _is_triple_plus_two(reals):
@@ -156,13 +159,13 @@ static func _eval_straight(reals: Array[Card], wilds: Array[Card], n: int, confi
return _make_result(_C.TYPE_STRAIGHT, max_rank, w == 0, reals + wilds)
return null
static func _eval_consecutive_pairs(reals: Array[Card], wilds: Array[Card], pair_count: int, config: RuleConfig) -> EvaluatedPlay:
static func _eval_consecutive_pairs(reals: Array[Card], wilds: Array[Card], pair_count: int, _config: RuleConfig) -> EvaluatedPlay:
if reals.size() + wilds.size() != pair_count * 2: return null
if wilds.is_empty() and _is_consecutive_pairs(reals, pair_count):
return _make_result(_C.TYPE_CONSECUTIVE_PAIRS, reals[reals.size()-1].rank(), true, reals)
return null
static func _eval_steel_plate(reals: Array[Card], wilds: Array[Card], triple_count: int, config: RuleConfig) -> EvaluatedPlay:
static func _eval_steel_plate(reals: Array[Card], wilds: Array[Card], triple_count: int, _config: RuleConfig) -> EvaluatedPlay:
if reals.size() + wilds.size() != triple_count * 3: return null
if wilds.is_empty() and _is_steel_plate(reals):
return _make_result(_C.TYPE_STEEL_PLATE, reals[reals.size()-1].rank(), true, reals)
@@ -180,7 +183,7 @@ static func _is_straight(cards: Array[Card], config: RuleConfig) -> bool:
if not config.straight_extends_to_ace and cards[cards.size()-1].rank() > 14: return false
return true
static func _is_consecutive_pairs(cards: Array[Card], pair_count: int) -> bool:
static func _is_consecutive_pairs(cards: Array[Card], _pair_count: int) -> bool:
for i in range(0, cards.size(), 2):
if cards[i].rank() != cards[i+1].rank(): return false
for i in range(0, cards.size() - 2, 2):

View File

@@ -11,7 +11,7 @@ var is_cleared: bool = false
func can_pass() -> bool:
return not table.is_empty()
func add_play(play: HandEvaluator.EvaluatedPlay, player_idx: int) -> void:
func add_play(play: HandEvaluator.EvaluatedPlay, _player_idx: int) -> void:
table.append(play)
if play.type == -1:
pass_count += 1

View File

@@ -12,7 +12,7 @@ static func can_play(
play: HandEvaluator.EvaluatedPlay,
table_history: Array,
last_player_idx: int,
current_rank: int,
_current_rank: int,
config: RuleConfig
) -> Dictionary:
if play.type == -1:

View File

@@ -12,7 +12,7 @@ signal game_ended(winner_team: int, reason: String)
var game_state: GameState
var ai_players: Dictionary = {}
var is_processing: bool = false
var _is_processing: bool = false
func start_game(config: RuleConfig, human_idx: int = 0, seed_: int = -1) -> void:
game_state = GameState.create(config, seed_)
@@ -20,37 +20,37 @@ func start_game(config: RuleConfig, human_idx: int = 0, seed_: int = -1) -> void
for i in range(4):
if not game_state.player_human[i]:
ai_players[i] = L2RuleAI.new()
var deck := Deck.create(game_state.seed)
var deck := Deck.create(game_state._seed)
game_state.deal_cards(deck)
game_state.phase = GameState.Phase.PLAY
game_state.round.active_player_idx = 0
game_state._round.active_player_idx = 0
state_changed.emit()
func handle_human_play(cards: Array) -> Dictionary:
if is_processing:
if _is_processing:
return {"ok": false, "error_code": 1, "data": null}
var hand := game_state.get_hand(game_state.round.active_player_idx)
var hand := game_state.get_hand(game_state._round.active_player_idx)
var play := HandEvaluator.evaluate(cards, game_state.current_rank, game_state.rule_config)
if play == null or play.type == -1:
return {"ok": false, "error_code": 1, "data": null}
var last_idx := game_state.round.last_non_pass_player()
var result := RuleEngine.can_play(hand, play, game_state.round.table, last_idx, game_state.current_rank, game_state.rule_config)
var last_idx := game_state._round.last_non_pass_player()
var result := RuleEngine.can_play(hand, play, game_state._round.table, last_idx, game_state.current_rank, game_state.rule_config)
if not result.ok:
return result
_apply_play(game_state.round.active_player_idx, play)
_apply_play(game_state._round.active_player_idx, play)
_advance_turn()
return {"ok": true, "error_code": 0, "data": null}
func handle_human_pass() -> Dictionary:
if is_processing:
if _is_processing:
return {"ok": false, "error_code": 1, "data": null}
var old_last := game_state.round.last_non_pass_player()
if old_last >= 0 and old_last == game_state.round.active_player_idx:
var old_last := game_state._round.last_non_pass_player()
if old_last >= 0 and old_last == game_state._round.active_player_idx:
return {"ok": false, "error_code": 3, "data": null}
var pass_play := HandEvaluator.EvaluatedPlay.new()
pass_play.type = -1
pass_play.primary_rank = 0
_apply_play(game_state.round.active_player_idx, pass_play)
_apply_play(game_state._round.active_player_idx, pass_play)
_advance_turn()
return {"ok": true, "error_code": 0, "data": null}
@@ -61,10 +61,10 @@ func _apply_play(player_idx: int, play: HandEvaluator.EvaluatedPlay) -> void:
action.player_idx = player_idx
action.action_type = "PASS" if play.type == -1 else "PLAY"
action.cards = play.cards.duplicate(false)
action.seq_id = game_state.round.next_seq()
action.timestamp = Time.get_unix_time_from_system()
action.seq_id = game_state._round.next_seq()
action.timestamp = Time.get_unix_time_from_system() as int
game_state.action_log.append(action)
game_state.round.add_play(play, player_idx)
game_state._round.add_play(play, player_idx)
var hand: Array = game_state.get_hand(player_idx)
if hand.is_empty() and not game_state.is_player_finished(player_idx):
game_state.add_finished_player(player_idx)
@@ -77,19 +77,19 @@ func _apply_play(player_idx: int, play: HandEvaluator.EvaluatedPlay) -> void:
EventBus.bomb_detonated.emit(player_idx, play.primary_rank)
func _advance_turn() -> void:
var hand: Array = game_state.get_hand(game_state.round.active_player_idx)
var hand: Array = game_state.get_hand(game_state._round.active_player_idx)
if hand.is_empty():
var partner := game_state.get_partner(game_state.round.active_player_idx)
var partner := game_state.get_partner(game_state._round.active_player_idx)
if not game_state.is_player_finished(partner):
game_state.round.active_player_idx = partner
game_state.round.reset_for_new_round()
game_state._round.active_player_idx = partner
game_state._round.reset_for_new_round()
else:
_next_alive_player()
elif game_state.round.is_cleared:
game_state.round.reset_for_new_round()
elif game_state._round.is_cleared:
game_state._round.reset_for_new_round()
else:
_next_alive_player()
var current := game_state.round.active_player_idx
var current := game_state._round.active_player_idx
turn_ready.emit(current, game_state.player_human[current])
EventBus.turn_changed.emit(current)
if not game_state.player_human[current]:
@@ -97,8 +97,8 @@ func _advance_turn() -> void:
func _next_alive_player() -> void:
for _i in range(4):
game_state.round.active_player_idx = (game_state.round.active_player_idx + 1) % 4
if not game_state.is_player_finished(game_state.round.active_player_idx):
game_state._round.active_player_idx = (game_state._round.active_player_idx + 1) % 4
if not game_state.is_player_finished(game_state._round.active_player_idx):
return
func _trigger_ai(player_idx: int) -> void:
@@ -106,7 +106,7 @@ func _trigger_ai(player_idx: int) -> void:
if ai == null:
return
var hand8: Array = game_state.get_hand(player_idx)
var decision: HandEvaluator.EvaluatedPlay = ai.decide(hand8, game_state.round.table, game_state.current_rank, game_state.rule_config)
var decision: HandEvaluator.EvaluatedPlay = ai.decide(hand8, game_state._round.table, game_state.current_rank, game_state.rule_config)
if decision.type == -1:
_apply_play(player_idx, decision)
else:

View File

@@ -2,15 +2,13 @@
class_name TrainingController
extends GameController
var _current_hint: HandEvaluator.EvaluatedPlay = null
func get_hint() -> HandEvaluator.EvaluatedPlay:
var hand := game_state.get_hand(game_state.round.active_player_idx)
var hand := game_state.get_hand(game_state._round.active_player_idx)
if hand.is_empty():
return null
var ai := L2RuleAI.new()
return ai.decide(hand, game_state.round.table, game_state.current_rank, game_state.rule_config)
return ai.decide(hand, game_state._round.table, game_state.current_rank, game_state.rule_config)
func get_all_legal_moves() -> Array:
var hand := game_state.get_hand(game_state.round.active_player_idx)
var hand := game_state.get_hand(game_state._round.active_player_idx)
return MoveGenerator.generate(hand, game_state.current_rank, game_state.rule_config)

View File

@@ -7,8 +7,12 @@ signal card_double_clicked(card_node: CardNode)
var card_data: Card = null
var is_selected: bool = false
@onready var texture_rect: TextureRect = $TextureRect
@onready var label: Label = $Label
@onready var panel: Panel = $Panel
@onready var rank_label: Label = $Panel/RankLabel
@onready var suit_label: Label = $Panel/SuitLabel
const SUIT_SYMBOLS := ["\u2660", "\u2665", "\u2663", "\u2666", "SJ", "BJ"]
const RANK_NAMES := ["", "", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "SJ", "BJ"]
func setup(card: Card) -> void:
card_data = card
@@ -17,17 +21,43 @@ func setup(card: Card) -> void:
func update_display() -> void:
if card_data == null:
return
var suits := ["S", "H", "C", "D", "SJ", "BJ"]
var ranks := ["", "", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "SJ", "BJ"]
var suit := card_data.suit()
var rank := card_data.rank()
if rank < ranks.size() and suit < suits.size() and label:
label.text = "%s %s" % [suits[suit], ranks[rank]]
modulate = Color.WHITE if not is_selected else Color(1.2, 1.2, 0.8)
var is_red := suit == 1 or suit == 3
var color := Color.RED if is_red else Color.BLACK
if rank_label:
rank_label.text = RANK_NAMES[rank]
rank_label.add_theme_color_override("font_color", color)
rank_label.add_theme_font_size_override("font_size", 14)
if suit_label:
suit_label.text = SUIT_SYMBOLS[suit]
suit_label.add_theme_color_override("font_color", color)
suit_label.add_theme_font_size_override("font_size", 24)
_update_panel()
func _update_panel() -> void:
if panel == null:
return
var sbox := StyleBoxFlat.new()
sbox.bg_color = Color.WHITE if not is_selected else Color(1.0, 1.0, 0.6)
sbox.set_corner_radius_all(6)
sbox.border_width_left = 2
sbox.border_width_right = 2
sbox.border_width_top = 2
sbox.border_width_bottom = 2
sbox.border_color = Color(0.3, 0.3, 0.3)
sbox.content_margin_left = 2
sbox.content_margin_right = 2
sbox.content_margin_top = 2
sbox.content_margin_bottom = 2
panel.add_theme_stylebox_override("panel", sbox)
func set_selected(sel: bool) -> void:
is_selected = sel
update_display()
_update_panel()
func _on_gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton:

View File

@@ -1,16 +1,40 @@
[gd_scene load_steps=2 format=3 uid="uid://card_node"]
[ext_resource type="Script" path="res://src/ui/components/card_node.gd" id="1_script"]
[node name="CardNode" type="Control"]
custom_minimum_size = Vector2(80, 120)
size = Vector2(80, 120)
custom_minimum_size = Vector2(70, 100)
mouse_filter = 1
size = Vector2(70, 100)
script = ExtResource("1_script")
[node name="TextureRect" type="TextureRect" parent="."]
layout_mode = 0
offset_right = 80.0
offset_bottom = 120.0
[node name="Label" type="Label" parent="."]
layout_mode = 0
offset_right = 80.0
offset_bottom = 120.0
[node name="Panel" type="Panel" parent="."]
layout_mode = 1
mouse_filter = 2
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
[node name="RankLabel" type="Label" parent="Panel"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -18.0
offset_right = 12.0
offset_bottom = -2.0
text = "A"
horizontal_alignment = 1
[node name="SuitLabel" type="Label" parent="Panel"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_right = 0.5
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
horizontal_alignment = 1
vertical_alignment = 1

View File

@@ -13,17 +13,22 @@ const CARD_SCENE := preload("res://src/ui/components/card_node.tscn")
var training_controller: TrainingController = null
func update_hand(hand: Array) -> void:
print("[DEBUG] HandArea.update_hand called, cards count: ", hand.size())
for cn in card_nodes:
cn.queue_free()
card_nodes.clear()
selected_cards.clear()
for c in hand:
var node := CARD_SCENE.instantiate() as CardNode
if node == null:
print("[DEBUG] CardNode instantiate returned null!")
continue
add_child(node)
node.setup(c)
node.card_clicked.connect(_on_card_clicked)
node.card_double_clicked.connect(_on_card_double_clicked)
add_child(node)
card_nodes.append(node)
print("[DEBUG] CardNodes created: ", card_nodes.size())
func _on_card_clicked(card_node: CardNode) -> void:
card_node.set_selected(not card_node.is_selected)

View File

@@ -65,10 +65,14 @@ func _on_hint_pressed() -> void:
hand_area.selected_cards.append(card)
if status_label: status_label.text = "建议牌型: %s (rank=%d)" % [hint.type, hint.primary_rank]
func _on_game_ended(winner_team: int, reason: String) -> void:
func _on_game_ended(winner_team: int, _reason: String) -> void:
if status_label: status_label.text = "游戏结束! 队伍 %d 获胜" % winner_team
if hand_area: hand_area.disable_input()
func _refresh_ui() -> void:
if controller and controller.game_state and hand_area:
hand_area.update_hand(controller.game_state.get_hand(0))
var hand: Array = controller.game_state.get_hand(0)
print("[DEBUG] _refresh_ui: hand size = ", hand.size())
hand_area.update_hand(hand)
else:
print("[DEBUG] _refresh_ui: controller=", controller, " game_state=", controller.game_state if controller else null, " hand_area=", hand_area)