feat: Implement Tekton Doors game mode with portal mechanics, add an in-game message bar, and introduce a pre-game countdown.

This commit is contained in:
Yogi Wiguna
2026-02-25 16:58:59 +08:00
parent a157c1efc4
commit 6d41f9dbc6
9 changed files with 513 additions and 8 deletions
+64 -2
View File
@@ -13,6 +13,7 @@ var camera_context_manager
var stop_n_go_manager
var stop_n_go_winner_id: int = -1 # Track who finished first in Stop n Go mode
var obstacle_manager
var portal_mode_manager
# Minimal local state
var _connection_check_timer: float = 0.0
@@ -70,6 +71,7 @@ func _ready():
stand_spawner.name = "StandSpawner"
stand_spawner.spawn_path = NodePath("../Stands") # Relative to Spawner, finding sibling
stand_spawner.add_spawnable_scene("res://scenes/static_tekton_stand.tscn")
stand_spawner.add_spawnable_scene("res://scenes/portal_door.tscn")
add_child(stand_spawner)
func _on_goal_count_updated(peer_id: int, count: int):
@@ -97,6 +99,13 @@ func _init_managers():
add_child(stop_n_go_manager)
# No direct initialize() yet, but we'll call start_game_mode later
# Portal manager for Tekton Doors mode
if LobbyManager.game_mode == "Tekton Doors":
portal_mode_manager = load("res://scripts/managers/portal_mode_manager.gd").new()
portal_mode_manager.name = "PortalModeManager"
add_child(portal_mode_manager)
portal_mode_manager.initialize(self, $EnhancedGridMap)
# Screen shake manager for impact feedback
screen_shake_manager = load("res://scripts/managers/screen_shake.gd").new()
screen_shake_manager.name = "ScreenShakeManager"
@@ -607,8 +616,9 @@ func _start_game():
if LobbyManager.game_mode == "Stop n Go" and stop_n_go_manager:
stop_n_go_manager.setup_mission_tiles()
# Spawn Static Tektons BEFORE countdown (Free Mode only)
if LobbyManager.game_mode != "Stop n Go":
# Spawn Static Tektons BEFORE countdown (Free Mode Only)
# Exclude for Stop n Go and Tekton Doors
if LobbyManager.game_mode != "Stop n Go" and LobbyManager.game_mode != "Tekton Doors":
spawn_static_tektons()
await _start_pre_game_countdown()
@@ -632,6 +642,13 @@ func _start_game():
if goals_cycle_manager:
var match_duration = LobbyManager.get_match_duration()
goals_cycle_manager.start_match(float(match_duration), false) # No cycles for Stop n Go
elif LobbyManager.game_mode == "Tekton Doors":
if portal_mode_manager:
portal_mode_manager.start_game_mode()
if goals_cycle_manager:
var match_duration = LobbyManager.get_match_duration()
goals_cycle_manager.start_match(float(match_duration))
elif goals_cycle_manager:
var match_duration = LobbyManager.get_match_duration()
goals_cycle_manager.start_match(float(match_duration))
@@ -663,6 +680,12 @@ func _assign_random_spawn_positions():
var all_players = get_tree().get_nodes_in_group("Players")
_assign_stop_n_go_spawn_positions(all_players)
return
# Tekton Doors Custom Spawn Logic
if LobbyManager.game_mode == "Tekton Doors":
var all_players = get_tree().get_nodes_in_group("Players")
_assign_portal_mode_spawn_positions(all_players)
return
var mid_x = enhanced_gridmap.columns / 2
var mid_z = enhanced_gridmap.rows / 2
@@ -780,6 +803,33 @@ func _assign_stop_n_go_spawn_positions(all_players: Array):
spawn_index += 1
print("[StopNGo] Assigned fixed starting block %s to player %s" % [assigned_pos, player.name])
func _assign_portal_mode_spawn_positions(all_players: Array):
"""Assigns spawns to different quadrants for Tekton Doors mode."""
if not portal_mode_manager:
_assign_random_spawn_positions() # Fallback
return
# Sort players for deterministic assignment
all_players.sort_custom(func(a, b): return a.name.to_int() < b.name.to_int())
var spawn_points = portal_mode_manager.get_spawn_points()
var spawn_index = 0
for player in all_players:
var assigned_pos = spawn_points[spawn_index % spawn_points.size()]
# Sync
player.position = player.grid_to_world(assigned_pos)
player.current_position = assigned_pos
player.is_player_moving = false
player.spawn_point_selected = true
if can_rpc():
player.rpc("set_spawn_position", assigned_pos)
spawn_index += 1
print("[PortalMode] Assigned Room Quadrant %s to player %s" % [assigned_pos, player.name])
# =============================================================================
# Tekton NPC Management
# =============================================================================
@@ -2118,3 +2168,15 @@ func can_rpc() -> bool:
if nakama and nakama.has_method("is_connected_to_nakama") and not nakama.is_connected_to_nakama():
return false
return true
@rpc("authority", "call_local", "reliable")
func display_message(message: String, type: int = 0):
"""Broadcasts a message to the local player's UI. This is called via main.rpc from various managers."""
# Find local player
var all_players = get_tree().get_nodes_in_group("Players")
for player in all_players:
# Check if this player is controlled by THIS client
if player.is_multiplayer_authority():
if player.has_method("display_message"):
player.display_message(message, type)
break