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:
@@ -27,7 +27,6 @@ AuthManager="*res://scripts/managers/auth_manager.gd"
|
|||||||
LobbyManager="*res://scripts/managers/lobby_manager.gd"
|
LobbyManager="*res://scripts/managers/lobby_manager.gd"
|
||||||
UserProfileManager="*res://scripts/managers/user_profile_manager.gd"
|
UserProfileManager="*res://scripts/managers/user_profile_manager.gd"
|
||||||
GameStateManager="*res://scripts/managers/game_state_manager.gd"
|
GameStateManager="*res://scripts/managers/game_state_manager.gd"
|
||||||
NetworkManager="*res://scripts/network_manager.gd"
|
|
||||||
TurnManager="*res://scripts/managers/turn_manager.gd"
|
TurnManager="*res://scripts/managers/turn_manager.gd"
|
||||||
GoalManager="*res://scripts/managers/goal_manager.gd"
|
GoalManager="*res://scripts/managers/goal_manager.gd"
|
||||||
PlayerManager="*res://scripts/managers/player_manager.gd"
|
PlayerManager="*res://scripts/managers/player_manager.gd"
|
||||||
|
|||||||
+1
-1
@@ -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://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="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"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_tekton"]
|
||||||
size = Vector3(0.8, 1, 0.8)
|
size = Vector3(0.8, 1, 0.8)
|
||||||
|
|||||||
@@ -58,8 +58,14 @@ func _ready():
|
|||||||
queue_free()
|
queue_free()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Wait for actor to be fully ready (player._ready awaits 0.5s then creates managers)
|
# Wait for actor to be fully ready (poll for EnhancedGridMap)
|
||||||
await get_tree().create_timer(1.5).timeout
|
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
|
enhanced_gridmap = actor.enhanced_gridmap
|
||||||
if not enhanced_gridmap:
|
if not enhanced_gridmap:
|
||||||
@@ -107,7 +113,7 @@ func _physics_process(delta):
|
|||||||
|
|
||||||
# Rate limiting (with difficulty scaling for Stop n Go)
|
# Rate limiting (with difficulty scaling for Stop n Go)
|
||||||
var current_tick_rate = tick_rate
|
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
|
current_tick_rate = int(tick_rate * 0.6) # 40% faster updates after column 10
|
||||||
|
|
||||||
_tick_counter += 1
|
_tick_counter += 1
|
||||||
@@ -133,7 +139,7 @@ func _run_ai_tick():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# STOP N GO: Don't process if already at finish line
|
# 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
|
return
|
||||||
|
|
||||||
# STOP N GO: Red light freezing logic
|
# 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])
|
print("[BotController] %s AI Tick. Goals: %s, Fullness: %.2f" % [actor.name, actor.goals, board_fullness])
|
||||||
|
|
||||||
# 0. BOT AGGRESSION THRESHOLD (Stop n Go)
|
# 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
|
var can_be_aggressive = not is_sng or actor.current_position.x > 10
|
||||||
|
|
||||||
# PRIORITY OVERRIDE: If board is getting full, prioritize clearing space!
|
# 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)
|
var next_step = Vector2i(path[1].x, path[1].y)
|
||||||
|
|
||||||
# STOP N GO BOUNDARY PROTECTION
|
# 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 main = get_tree().root.get_node_or_null("Main")
|
||||||
var gc_manager = main.get_node_or_null("GoalsCycleManager") if main else null
|
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
|
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:
|
func _is_goals_achieved() -> bool:
|
||||||
"""Check if goal pattern is complete (Standard) or mission complete (Stop n Go)."""
|
"""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")
|
var main = get_tree().root.get_node_or_null("Main")
|
||||||
if main:
|
if main:
|
||||||
var sng_manager = main.get_node_or_null("StopNGoManager")
|
var sng_manager = main.get_node_or_null("StopNGoManager")
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ func find_nearest_tile_of_type(tile_types: Array) -> Vector2i:
|
|||||||
var found_in_layer = []
|
var found_in_layer = []
|
||||||
|
|
||||||
# In Stop n Go, prefer tiles "ahead" (higher X)
|
# 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
|
# Check the ring
|
||||||
for x_off in range(-r, r + 1):
|
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:
|
func find_optimal_move_target() -> Vector2i:
|
||||||
"""Calculate the best position to move towards."""
|
"""Calculate the best position to move towards."""
|
||||||
var main = actor.get_tree().get_root().get_node_or_null("Main")
|
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 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 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
|
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
|
return result
|
||||||
|
|
||||||
# 0. STOP N GO THRESHOLD: No sabotage until passing column 10
|
# 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
|
return result
|
||||||
|
|
||||||
# Get opponents
|
# Get opponents
|
||||||
@@ -540,7 +540,7 @@ func evaluate_sabotage_opportunity() -> Dictionary:
|
|||||||
|
|
||||||
# Condition 2: Opponent is close to completing their goal
|
# Condition 2: Opponent is close to completing their goal
|
||||||
var progress_threshold = 0.7
|
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!
|
progress_threshold = 0.4 # More aggressive in late game!
|
||||||
|
|
||||||
for opponent in opponents:
|
for opponent in opponents:
|
||||||
@@ -563,7 +563,7 @@ func evaluate_sabotage_opportunity() -> Dictionary:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# Condition 4: Random Aggression (Stop n Go Late Game)
|
# 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
|
if randf() < 0.3: # 30% chance each tick to just be mean
|
||||||
result.should_sabotage = true
|
result.should_sabotage = true
|
||||||
result.reason = "random_aggression"
|
result.reason = "random_aggression"
|
||||||
|
|||||||
@@ -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"]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cx8k9m2p5yqjw
|
||||||
@@ -75,9 +75,10 @@ func _update_camera_target():
|
|||||||
var current_row = player.current_position.y
|
var current_row = player.current_position.y
|
||||||
var current_col = player.current_position.x
|
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)
|
_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()
|
_update_tekton_doors_camera()
|
||||||
else:
|
else:
|
||||||
_update_freemode_camera(current_row, current_col)
|
_update_freemode_camera(current_row, current_col)
|
||||||
|
|||||||
@@ -46,6 +46,12 @@ var selected_area: String = "Desert" # Host-controlled
|
|||||||
var game_mode: String = "Freemode" # Host-controlled
|
var game_mode: String = "Freemode" # Host-controlled
|
||||||
var local_character_index: int = 0 # Local player's character index
|
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
|
# Signals
|
||||||
signal game_mode_changed(mode: String)
|
signal game_mode_changed(mode: String)
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ func handle_unhandled_input(event):
|
|||||||
if event is InputEventKey and event.pressed and not event.echo:
|
if event is InputEventKey and event.pressed and not event.echo:
|
||||||
match event.keycode:
|
match event.keycode:
|
||||||
KEY_KP_1, KEY_1, KEY_KP_2, KEY_2, KEY_KP_3, KEY_3, KEY_KP_4, KEY_4:
|
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:
|
match event.keycode:
|
||||||
KEY_KP_1, KEY_1:
|
KEY_KP_1, KEY_1:
|
||||||
player.activate_powerup(0) # FASTER_SPEED
|
player.activate_powerup(0) # FASTER_SPEED
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
|||||||
|
|
||||||
# SCORING: 200 Points for successful attack (ONLY in Free Mode)
|
# SCORING: 200 Points for successful attack (ONLY in Free Mode)
|
||||||
if player.is_multiplayer_authority():
|
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:
|
if not is_sng:
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
if main:
|
if main:
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ func _check_and_refill_grid_if_needed(server_gridmap: Node):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not has_items:
|
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
|
# Tekton Doors handles its own wall-aware refill in PortalModeManager
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ func _execute_area_freeze(center_pos: Vector2i = Vector2i.ZERO):
|
|||||||
hit_count += 1
|
hit_count += 1
|
||||||
|
|
||||||
if hit_count > 0 and player.is_multiplayer_authority():
|
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:
|
if not is_sng:
|
||||||
var points = hit_count * 50
|
var points = hit_count * 50
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
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)
|
item_id = rng.randi_range(7, 10)
|
||||||
else:
|
else:
|
||||||
# 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes)
|
# 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:
|
if is_restricted:
|
||||||
item_id = [11, 12, 14].pick_random()
|
item_id = [11, 12, 14].pick_random()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String):
|
|||||||
if btn.has_node("ShortcutLabel"):
|
if btn.has_node("ShortcutLabel"):
|
||||||
# Update Label content if it exists to match potential remapping
|
# Update Label content if it exists to match potential remapping
|
||||||
var existing_lbl = btn.get_node("ShortcutLabel")
|
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:
|
match button_name:
|
||||||
"Grab": existing_lbl.text = "Space" if is_sng else ""
|
"Grab": existing_lbl.text = "Space" if is_sng else ""
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ static func get_tile_weights() -> Dictionary:
|
|||||||
weights[tile] = STANDARD_WEIGHT
|
weights[tile] = STANDARD_WEIGHT
|
||||||
|
|
||||||
# Special tiles
|
# 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:
|
for tile in SPECIAL_TILES:
|
||||||
if is_restricted and tile == TILE_WALL:
|
if is_restricted and tile == TILE_WALL:
|
||||||
continue # Hide Wall Block only in restricted modes
|
continue # Hide Wall Block only in restricted modes
|
||||||
|
|||||||
@@ -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 +0,0 @@
|
|||||||
uid://l4qd7n8l2hch
|
|
||||||
@@ -32,7 +32,8 @@ func _ready():
|
|||||||
var wall_btn = container.get_node_or_null("WallBtn")
|
var wall_btn = container.get_node_or_null("WallBtn")
|
||||||
_setup_btn(2, wall_btn)
|
_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:
|
if wall_btn and is_restricted:
|
||||||
wall_btn.visible = false # Hide Wall Power-up in restricted modes
|
wall_btn.visible = false # Hide Wall Power-up in restricted modes
|
||||||
_setup_btn(3, container.get_node_or_null("GhostBtn"))
|
_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
|
# Determine Label Text based on Effect ID
|
||||||
var key_text = ""
|
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:
|
if is_restricted:
|
||||||
# Restricted Mapping: 1, 2, 3 (No Wall)
|
# Restricted Mapping: 1, 2, 3 (No Wall)
|
||||||
match effect_id:
|
match effect_id:
|
||||||
|
|||||||
Reference in New Issue
Block a user