feat: Implement core playerboard management including item grabbing, power-up handling, goal completion, and grid refilling, alongside new Tekton entity and various game managers.

This commit is contained in:
2026-03-02 03:10:38 +08:00
parent 1c5c3d47f6
commit a7442bb1a6
17 changed files with 78 additions and 42 deletions
-1
View File
@@ -27,7 +27,6 @@ AuthManager="*res://scripts/managers/auth_manager.gd"
LobbyManager="*res://scripts/managers/lobby_manager.gd"
UserProfileManager="*res://scripts/managers/user_profile_manager.gd"
GameStateManager="*res://scripts/managers/game_state_manager.gd"
NetworkManager="*res://scripts/network_manager.gd"
TurnManager="*res://scripts/managers/turn_manager.gd"
GoalManager="*res://scripts/managers/goal_manager.gd"
PlayerManager="*res://scripts/managers/player_manager.gd"
+1 -1
View File
@@ -2,7 +2,7 @@
[ext_resource type="Script" uid="uid://dyovwailce5tf" path="res://scripts/tekton.gd" id="1_tekton"]
[ext_resource type="Script" uid="uid://c67yq846u8y68" path="res://scripts/tekton_controller.gd" id="2_controller"]
[ext_resource type="PackedScene" uid="uid://b6d6qu1ir13q1" path="res://scenes/tekton_mesh.scn" id="3_d2kpk"]
[ext_resource type="PackedScene" uid="uid://b6d6qu1ir13q1" path="res://scenes/tekton_mesh.tscn" id="3_d2kpk"]
[sub_resource type="BoxShape3D" id="BoxShape3D_tekton"]
size = Vector3(0.8, 1, 0.8)
+13 -7
View File
@@ -58,8 +58,14 @@ func _ready():
queue_free()
return
# Wait for actor to be fully ready (player._ready awaits 0.5s then creates managers)
await get_tree().create_timer(1.5).timeout
# Wait for actor to be fully ready (poll for EnhancedGridMap)
var wait_timeout = 3.0
var wait_elapsed = 0.0
while wait_elapsed < wait_timeout:
await get_tree().create_timer(0.1).timeout
wait_elapsed += 0.1
if actor and actor.get("enhanced_gridmap") and actor.enhanced_gridmap:
break
enhanced_gridmap = actor.enhanced_gridmap
if not enhanced_gridmap:
@@ -107,7 +113,7 @@ func _physics_process(delta):
# Rate limiting (with difficulty scaling for Stop n Go)
var current_tick_rate = tick_rate
if LobbyManager.game_mode == "Stop n Go" and actor.current_position.x > 10:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x > 10:
current_tick_rate = int(tick_rate * 0.6) # 40% faster updates after column 10
_tick_counter += 1
@@ -133,7 +139,7 @@ func _run_ai_tick():
return
# STOP N GO: Don't process if already at finish line
if LobbyManager.game_mode == "Stop n Go" and actor.current_position.x >= 21:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x >= 21:
return
# STOP N GO: Red light freezing logic
@@ -150,7 +156,7 @@ func _run_ai_tick():
print("[BotController] %s AI Tick. Goals: %s, Fullness: %.2f" % [actor.name, actor.goals, board_fullness])
# 0. BOT AGGRESSION THRESHOLD (Stop n Go)
var is_sng = LobbyManager.game_mode == "Stop n Go"
var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
var can_be_aggressive = not is_sng or actor.current_position.x > 10
# PRIORITY OVERRIDE: If board is getting full, prioritize clearing space!
@@ -289,7 +295,7 @@ func _try_attack_chase() -> bool:
var next_step = Vector2i(path[1].x, path[1].y)
# STOP N GO BOUNDARY PROTECTION
if LobbyManager.game_mode == "Stop n Go" and next_step.x >= 21:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and next_step.x >= 21:
var main = get_tree().root.get_node_or_null("Main")
var gc_manager = main.get_node_or_null("GoalsCycleManager") if main else null
var time_remaining = gc_manager.get_global_time_remaining() if gc_manager else 999.0
@@ -683,7 +689,7 @@ func _get_board_fullness_ratio() -> float:
func _is_goals_achieved() -> bool:
"""Check if goal pattern is complete (Standard) or mission complete (Stop n Go)."""
if LobbyManager.game_mode == "Stop n Go":
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO):
var main = get_tree().root.get_node_or_null("Main")
if main:
var sng_manager = main.get_node_or_null("StopNGoManager")
+5 -5
View File
@@ -255,7 +255,7 @@ func find_nearest_tile_of_type(tile_types: Array) -> Vector2i:
var found_in_layer = []
# In Stop n Go, prefer tiles "ahead" (higher X)
var is_stop_n_go = LobbyManager.game_mode == "Stop n Go"
var is_stop_n_go = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
# Check the ring
for x_off in range(-r, r + 1):
@@ -311,7 +311,7 @@ func _check_spiral_cell(x: int, z: int, tile_types: Array, result_array: Array):
func find_optimal_move_target() -> Vector2i:
"""Calculate the best position to move towards."""
var main = actor.get_tree().get_root().get_node_or_null("Main")
var is_sng = LobbyManager.game_mode == "Stop n Go"
var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
var gc_manager = main.get_node_or_null("GoalsCycleManager") if main else null
var time_left = gc_manager.get_global_time_remaining() if gc_manager else 999.0
var is_match_running = gc_manager.is_match_running() if gc_manager else false
@@ -521,7 +521,7 @@ func evaluate_sabotage_opportunity() -> Dictionary:
return result
# 0. STOP N GO THRESHOLD: No sabotage until passing column 10
if LobbyManager.game_mode == "Stop n Go" and actor.current_position.x <= 10:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x <= 10:
return result
# Get opponents
@@ -540,7 +540,7 @@ func evaluate_sabotage_opportunity() -> Dictionary:
# Condition 2: Opponent is close to completing their goal
var progress_threshold = 0.7
if LobbyManager.game_mode == "Stop n Go" and actor.current_position.x > 10:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x > 10:
progress_threshold = 0.4 # More aggressive in late game!
for opponent in opponents:
@@ -563,7 +563,7 @@ func evaluate_sabotage_opportunity() -> Dictionary:
return result
# Condition 4: Random Aggression (Stop n Go Late Game)
if LobbyManager.game_mode == "Stop n Go" and actor.current_position.x > 12:
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x > 12:
if randf() < 0.3: # 30% chance each tick to just be mean
result.should_sabotage = true
result.reason = "random_aggression"
+35
View File
@@ -0,0 +1,35 @@
class_name GameMode extends RefCounted
enum Mode {
FREEMODE = 0,
STOP_N_GO = 1,
TEKTON_DOORS = 2
}
static func from_string(mode: String) -> Mode:
match mode:
"Freemode":
return Mode.FREEMODE
"Stop n Go":
return Mode.STOP_N_GO
"Tekton Doors":
return Mode.TEKTON_DOORS
_:
return Mode.FREEMODE
static func mode_to_string(mode: Mode) -> String:
match mode:
Mode.FREEMODE:
return "Freemode"
Mode.STOP_N_GO:
return "Stop n Go"
Mode.TEKTON_DOORS:
return "Tekton Doors"
_:
return "Freemode"
static func is_restricted(mode: Mode) -> bool:
return mode == Mode.STOP_N_GO or mode == Mode.TEKTON_DOORS
static func get_all_modes() -> Array[String]:
return ["Freemode", "Stop n Go", "Tekton Doors"]
+1
View File
@@ -0,0 +1 @@
uid://cx8k9m2p5yqjw
+3 -2
View File
@@ -75,9 +75,10 @@ func _update_camera_target():
var current_row = player.current_position.y
var current_col = player.current_position.x
if LobbyManager.game_mode == "Stop n Go":
var mode = LobbyManager.get_game_mode()
if mode == GameMode.Mode.STOP_N_GO:
_update_stop_n_go_camera(current_row, current_col)
elif LobbyManager.game_mode == "Tekton Doors":
elif mode == GameMode.Mode.TEKTON_DOORS:
_update_tekton_doors_camera()
else:
_update_freemode_camera(current_row, current_col)
+6
View File
@@ -46,6 +46,12 @@ var selected_area: String = "Desert" # Host-controlled
var game_mode: String = "Freemode" # Host-controlled
var local_character_index: int = 0 # Local player's character index
func get_game_mode() -> GameMode.Mode:
return GameMode.from_string(game_mode)
func is_game_mode(mode: GameMode.Mode) -> bool:
return get_game_mode() == mode
# Signals
signal game_mode_changed(mode: String)
+2 -1
View File
@@ -90,7 +90,8 @@ func handle_unhandled_input(event):
if event is InputEventKey and event.pressed and not event.echo:
match event.keycode:
KEY_KP_1, KEY_1, KEY_KP_2, KEY_2, KEY_KP_3, KEY_3, KEY_KP_4, KEY_4:
var is_restricted = LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors"
var mode = LobbyManager.get_game_mode()
var is_restricted = GameMode.is_restricted(mode)
match event.keycode:
KEY_KP_1, KEY_1:
player.activate_powerup(0) # FASTER_SPEED
+1 -1
View File
@@ -219,7 +219,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
# SCORING: 200 Points for successful attack (ONLY in Free Mode)
if player.is_multiplayer_authority():
var is_sng = LobbyManager.game_mode == "Stop n Go"
var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
if not is_sng:
var main = player.get_tree().get_root().get_node_or_null("Main")
if main:
+1 -1
View File
@@ -232,7 +232,7 @@ func _check_and_refill_grid_if_needed(server_gridmap: Node):
break
if not has_items:
if LobbyManager.game_mode == "Tekton Doors":
if LobbyManager.is_game_mode(GameMode.Mode.TEKTON_DOORS):
# Tekton Doors handles its own wall-aware refill in PortalModeManager
return
+3 -2
View File
@@ -315,7 +315,7 @@ func _execute_area_freeze(center_pos: Vector2i = Vector2i.ZERO):
hit_count += 1
if hit_count > 0 and player.is_multiplayer_authority():
var is_sng = LobbyManager.game_mode == "Stop n Go"
var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
if not is_sng:
var points = hit_count * 50
var main = player.get_tree().get_root().get_node_or_null("Main")
@@ -462,7 +462,8 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
item_id = rng.randi_range(7, 10)
else:
# 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes)
var is_restricted = LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors"
var mode = LobbyManager.get_game_mode()
var is_restricted = GameMode.is_restricted(mode)
if is_restricted:
item_id = [11, 12, 14].pick_random()
else:
+1 -1
View File
@@ -256,7 +256,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String):
if btn.has_node("ShortcutLabel"):
# Update Label content if it exists to match potential remapping
var existing_lbl = btn.get_node("ShortcutLabel")
var is_sng = LobbyManager.game_mode == "Stop n Go"
var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)
match button_name:
"Grab": existing_lbl.text = "Space" if is_sng else ""
+2 -1
View File
@@ -50,7 +50,8 @@ static func get_tile_weights() -> Dictionary:
weights[tile] = STANDARD_WEIGHT
# Special tiles
var is_restricted = LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors"
var mode = LobbyManager.get_game_mode()
var is_restricted = GameMode.is_restricted(mode)
for tile in SPECIAL_TILES:
if is_restricted and tile == TILE_WALL:
continue # Hide Wall Block only in restricted modes
-16
View File
@@ -1,16 +0,0 @@
extends Node
# This script is deprecated in favor of NakamaManager.
# The logic has been moved to allow the NakamaMultiplayerBridge to handle
# the networking transparently.
func _ready():
pass
func host_game():
# Redirect to NakamaManager
NakamaManager.host_game()
func join_game(match_id):
# Redirect to NakamaManager
NakamaManager.join_game(match_id)
-1
View File
@@ -1 +0,0 @@
uid://l4qd7n8l2hch
+4 -2
View File
@@ -32,7 +32,8 @@ func _ready():
var wall_btn = container.get_node_or_null("WallBtn")
_setup_btn(2, wall_btn)
var is_restricted = LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors"
var mode = LobbyManager.get_game_mode()
var is_restricted = GameMode.is_restricted(mode)
if wall_btn and is_restricted:
wall_btn.visible = false # Hide Wall Power-up in restricted modes
_setup_btn(3, container.get_node_or_null("GhostBtn"))
@@ -104,7 +105,8 @@ func _setup_btn(effect_id: int, btn: Button):
# Determine Label Text based on Effect ID
var key_text = ""
var is_restricted = LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors"
var mode = LobbyManager.get_game_mode()
var is_restricted = GameMode.is_restricted(mode)
if is_restricted:
# Restricted Mapping: 1, 2, 3 (No Wall)
match effect_id: