feat: Add core player entity with movement, race, input, character selection, and multiplayer synchronization, integrating various game managers.

This commit is contained in:
Yogi Wiguna
2026-02-23 15:57:41 +08:00
parent 8a5c25be23
commit 3a5e6ad703
4 changed files with 54 additions and 66 deletions
+45 -59
View File
@@ -105,21 +105,20 @@ func _update_hud_visuals():
red_tint_overlay.visible = (current_phase == Phase.STOP)
var my_id = multiplayer.get_unique_id()
if mission_label and player_missions.has(my_id):
var mission = player_missions[my_id]
# Get Icon name or ID for display
var tile_name = "Items"
match mission["target_tile"]:
7: tile_name = "Hearts"
8: tile_name = "Diamonds"
9: tile_name = "Stars"
10: tile_name = "Coins"
mission_label.text = "Collect %d %s: %d / %d" % [mission["required"], tile_name, mission["current"], mission["required"]]
if mission_label:
var main = get_node_or_null("/root/Main")
var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager") if main else null
if mission["current"] >= mission["required"]:
mission_label.text = "MISSION COMPLETE! REACH FINISH!"
# Get count from GoalsCycleManager (Source of truth for PlayerBoardLabel)
var completed_count = goals_cycle_manager.player_goal_counts.get(my_id, 0) if goals_cycle_manager else 0
mission_label.text = "GOALS (%d/3)" % completed_count
if completed_count >= 3:
mission_label.text = "ALL GOALS COMPLETE!\nREACH THE FINISH!"
mission_label.add_theme_color_override("font_color", Color.GOLD)
else:
mission_label.add_theme_color_override("font_color", Color.WHITE)
func activate_client_side():
is_active = true
@@ -284,22 +283,9 @@ func _spawn_mission_tiles():
count += 1
func _assign_missions():
# Assign UNIQUE target tile types to players cyclicly
var players = GameStateManager.players
var tile_types = [ScarcityModel.TILE_HEART, ScarcityModel.TILE_DIAMOND, ScarcityModel.TILE_STAR, ScarcityModel.TILE_COIN]
var idx = 0
for p_id in players:
var target = tile_types[idx % tile_types.size()]
player_missions[p_id] = {
"target_tile": target,
"required": 3,
"current": 0
}
idx += 1
if can_rpc():
rpc("sync_missions", player_missions)
# NO-OP: Missions are now achievement-based (Complete 3 Goals)
# which is tracked natively by GoalsCycleManager.
pass
@rpc("authority", "call_local", "reliable")
func sync_missions(missions: Dictionary):
@@ -338,38 +324,38 @@ func _penalize_player(player_id: int):
# Notification is also handled inside on_stop_phase_violation on the player node
emit_signal("player_penalized", player_id)
func update_mission_progress(player_id: int, tile_id: int):
if not player_missions.has(player_id): return
var mission = player_missions[player_id]
if tile_id == mission["target_tile"]:
mission["current"] = min(mission["current"] + 1, mission["required"])
if mission["current"] >= mission["required"]:
emit_signal("mission_status_updated", player_id, true)
# FIX: NotificationManager.send_message_to_player() does NOT exist.
# We need to find the player node and use send_message(target, msg, type)
var main = get_node("/root/Main")
if main:
var player_node = main.get_node_or_null(str(player_id))
if player_node:
NotificationManager.send_message(player_node, "Mission Complete! Reach the Finish!", NotificationManager.MessageType.GOAL)
if multiplayer.is_server() and can_rpc():
rpc("sync_mission_progress", player_id, mission["current"])
func update_mission_progress(_player_id: int, _tile_id: int):
# Redundant in Board-based mode, but kept for compatibility.
# The board is synced separately via sync_playerboard in playerboard_manager.gd.
pass
@rpc("any_peer", "call_local", "reliable")
func sync_mission_progress(player_id: int, current: int):
if player_missions.has(player_id):
player_missions[player_id]["current"] = current
func sync_mission_progress(_player_id: int, _mission_index: int, _current: int):
# Deprecated
pass
func check_win_condition(player_id: int, position: Vector2i) -> bool:
if not player_missions.has(player_id): return false
# 1. Must reach the finish line (Column 21)
if position.x < finish_line_x:
return false
# 2. Must have 3 Goal Completions (tracked by GoalsCycleManager)
var main = get_node_or_null("/root/Main")
if not main: return false
var mission = player_missions[player_id]
if mission["current"] >= mission["required"]:
# Win when reaching X >= 21
if position.x >= finish_line_x:
return true
return false
var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager")
if not goals_cycle_manager: return false
var completed_count = goals_cycle_manager.player_goal_counts.get(player_id, 0)
if completed_count >= 3:
print("[StopNGo] Player %d REACHED FINISH with %d goals complete!" % [player_id, completed_count])
return true
else:
# Inform the player locally if they reach the end without goals
var player_node = main.get_node_or_null(str(player_id))
if player_node:
NotificationManager.send_message(player_node, "Incomplete! Achieve 3 goals (x3) to win!", NotificationManager.MessageType.WARNING)
print("[StopNGo] Player %d reached finish but goal count too low: %d/3" % [player_id, completed_count])
return false