From a7442bb1a63f45a6d0e6de7d2d63100e8b18fa43 Mon Sep 17 00:00:00 2001 From: adtpdn Date: Mon, 2 Mar 2026 03:10:38 +0800 Subject: [PATCH] feat: Implement core playerboard management including item grabbing, power-up handling, goal completion, and grid refilling, alongside new Tekton entity and various game managers. --- project.godot | 1 - scenes/tekton.tscn | 2 +- scripts/bot_controller.gd | 20 +++++++----- scripts/bot_strategic_planner.gd | 10 +++--- scripts/game_mode.gd | 35 +++++++++++++++++++++ scripts/game_mode.gd.uid | 1 + scripts/managers/camera_context_manager.gd | 5 +-- scripts/managers/lobby_manager.gd | 6 ++++ scripts/managers/player_input_manager.gd | 3 +- scripts/managers/player_movement_manager.gd | 2 +- scripts/managers/playerboard_manager.gd | 2 +- scripts/managers/special_tiles_manager.gd | 5 +-- scripts/managers/touch_controls.gd | 2 +- scripts/models/scarcity_model.gd | 3 +- scripts/network_manager.gd | 16 ---------- scripts/network_manager.gd.uid | 1 - scripts/ui/powerup_inventory_ui.gd | 6 ++-- 17 files changed, 78 insertions(+), 42 deletions(-) create mode 100644 scripts/game_mode.gd create mode 100644 scripts/game_mode.gd.uid delete mode 100644 scripts/network_manager.gd delete mode 100644 scripts/network_manager.gd.uid diff --git a/project.godot b/project.godot index 8526ac2..2099ab0 100644 --- a/project.godot +++ b/project.godot @@ -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" diff --git a/scenes/tekton.tscn b/scenes/tekton.tscn index 731e85f..26df3f0 100644 --- a/scenes/tekton.tscn +++ b/scenes/tekton.tscn @@ -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) diff --git a/scripts/bot_controller.gd b/scripts/bot_controller.gd index 533f0f0..f21447e 100644 --- a/scripts/bot_controller.gd +++ b/scripts/bot_controller.gd @@ -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") diff --git a/scripts/bot_strategic_planner.gd b/scripts/bot_strategic_planner.gd index bc5db12..939aa2b 100644 --- a/scripts/bot_strategic_planner.gd +++ b/scripts/bot_strategic_planner.gd @@ -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" diff --git a/scripts/game_mode.gd b/scripts/game_mode.gd new file mode 100644 index 0000000..15db93e --- /dev/null +++ b/scripts/game_mode.gd @@ -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"] diff --git a/scripts/game_mode.gd.uid b/scripts/game_mode.gd.uid new file mode 100644 index 0000000..05549f0 --- /dev/null +++ b/scripts/game_mode.gd.uid @@ -0,0 +1 @@ +uid://cx8k9m2p5yqjw \ No newline at end of file diff --git a/scripts/managers/camera_context_manager.gd b/scripts/managers/camera_context_manager.gd index e411a47..ffd5221 100644 --- a/scripts/managers/camera_context_manager.gd +++ b/scripts/managers/camera_context_manager.gd @@ -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) diff --git a/scripts/managers/lobby_manager.gd b/scripts/managers/lobby_manager.gd index 80ea62a..5242417 100644 --- a/scripts/managers/lobby_manager.gd +++ b/scripts/managers/lobby_manager.gd @@ -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) diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index c34011c..3da2287 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -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 diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index d482557..662e6d7 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -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: diff --git a/scripts/managers/playerboard_manager.gd b/scripts/managers/playerboard_manager.gd index 30c5f9b..6c982de 100644 --- a/scripts/managers/playerboard_manager.gd +++ b/scripts/managers/playerboard_manager.gd @@ -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 diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index afdd4ea..5b2ee42 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -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: diff --git a/scripts/managers/touch_controls.gd b/scripts/managers/touch_controls.gd index ee1b944..d5bf139 100644 --- a/scripts/managers/touch_controls.gd +++ b/scripts/managers/touch_controls.gd @@ -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 "" diff --git a/scripts/models/scarcity_model.gd b/scripts/models/scarcity_model.gd index 996b67f..485a5c1 100644 --- a/scripts/models/scarcity_model.gd +++ b/scripts/models/scarcity_model.gd @@ -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 diff --git a/scripts/network_manager.gd b/scripts/network_manager.gd deleted file mode 100644 index c2466e4..0000000 --- a/scripts/network_manager.gd +++ /dev/null @@ -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) diff --git a/scripts/network_manager.gd.uid b/scripts/network_manager.gd.uid deleted file mode 100644 index cf98f46..0000000 --- a/scripts/network_manager.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://l4qd7n8l2hch diff --git a/scripts/ui/powerup_inventory_ui.gd b/scripts/ui/powerup_inventory_ui.gd index c71dc16..b13e8fa 100644 --- a/scripts/ui/powerup_inventory_ui.gd +++ b/scripts/ui/powerup_inventory_ui.gd @@ -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: