feat: add battlepass slider and quest list tabs to lobby HUD
This commit is contained in:
@@ -79,7 +79,6 @@ func _ready():
|
||||
return
|
||||
|
||||
# Initialize strategic planner
|
||||
var BotStrategicPlanner = load("res://scripts/bot_strategic_planner.gd")
|
||||
strategic_planner = BotStrategicPlanner.new(actor, enhanced_gridmap)
|
||||
|
||||
# Disable input processing for bots
|
||||
@@ -211,7 +210,7 @@ func _run_ai_tick():
|
||||
return
|
||||
|
||||
# Priority 0.5: Gauntlet (#075) — use Ghost powerup if boxed in
|
||||
if await _try_activate_ghost():
|
||||
if _try_activate_ghost():
|
||||
print("[BotController] Action Taken: Ghost (trapped)")
|
||||
return
|
||||
|
||||
@@ -677,8 +676,6 @@ func _try_move() -> bool:
|
||||
else:
|
||||
print("[BotController] %s simple_move_to BLOCKED (others). Trying unstuck move." % actor.name)
|
||||
return await _try_unstuck_move()
|
||||
|
||||
return false
|
||||
|
||||
func _should_freeze_for_stop_n_go() -> bool:
|
||||
"""Check if the bot should intentionally skip its turn during STOP phase outside of safe zones."""
|
||||
@@ -909,12 +906,7 @@ func _handle_goal_completion():
|
||||
if goals_cycle_manager:
|
||||
var time_remaining = goals_cycle_manager.get_time_remaining()
|
||||
goals_cycle_manager.on_goal_completed(actor, time_remaining)
|
||||
|
||||
var powerup_manager = actor.get_node_or_null("PowerUpManager")
|
||||
# if powerup_manager:
|
||||
# powerup_manager.add_goal_completion_reward()
|
||||
|
||||
|
||||
|
||||
print("[BotController] %s COMPLETED GOAL!" % actor.name)
|
||||
|
||||
func _wait_with_variance(base_delay: float):
|
||||
|
||||
@@ -204,9 +204,7 @@ func get_unneeded_tile_slot() -> int:
|
||||
"""Find a slot containing a tile that is not needed for the goal."""
|
||||
if not actor or actor.playerboard.size() == 0:
|
||||
return -1
|
||||
|
||||
var needed_tiles = get_tiles_needed()
|
||||
|
||||
|
||||
# Check center 3x3 for misplaced tiles
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
@@ -252,7 +250,6 @@ func get_unneeded_tile_slot() -> int:
|
||||
var center_indices = [6, 7, 8, 11, 12, 13, 16, 17, 18]
|
||||
for i in range(actor.playerboard.size()):
|
||||
if not i in center_indices and actor.playerboard[i] != -1:
|
||||
var item = actor.playerboard[i]
|
||||
# Only keep if we strictly need it and can't find it easily?
|
||||
# Actually, generally dump outer ring tiles to keep board clean
|
||||
# unless we are about to move it to a valid spot.
|
||||
@@ -263,8 +260,6 @@ func get_unneeded_tile_slot() -> int:
|
||||
|
||||
return -1
|
||||
|
||||
return -1
|
||||
|
||||
func get_unneeded_tile_slot_panic() -> int:
|
||||
"""Aggressively find ANY tile that doesn't match a goal perfectly."""
|
||||
if not actor or actor.playerboard.size() == 0:
|
||||
@@ -439,33 +434,31 @@ func find_optimal_move_target() -> Vector2i:
|
||||
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_late_game = is_sng and is_match_running and time_left > 0.0 and time_left <= 30.0
|
||||
|
||||
# 1. STOP N GO: Reach the finish line if goals are complete
|
||||
if is_sng and main:
|
||||
var sng_manager = main.get_node_or_null("StopNGoManager")
|
||||
if sng_manager and sng_manager.is_mission_complete(actor.name.to_int()):
|
||||
# MISSION COMPLETE: We CAN finish, but should we?
|
||||
|
||||
if not is_late_game:
|
||||
# CHAOS MODE: Allow falling through to target holo tiles, but we'll limit the target X later
|
||||
# print("[BotStrategicPlanner] %s mission complete (Chaos Phase %.1fs). Roaming field." % [actor.name, time_left])
|
||||
pass
|
||||
else:
|
||||
# Late game: go to finish
|
||||
var finish_target = Vector2i(21, actor.current_position.y)
|
||||
|
||||
# Ensure finish_target is walkable
|
||||
if not _is_valid_move_target(finish_target):
|
||||
for dy in [1, -1, 2, -2]:
|
||||
var alt = Vector2i(21, actor.current_position.y + dy)
|
||||
if _is_valid_move_target(alt):
|
||||
finish_target = alt
|
||||
break
|
||||
|
||||
print("[BotStrategicPlanner] %s mission complete (Late Game %.1fs)! Heading to finish: %s" % [actor.name, time_left, finish_target])
|
||||
return finish_target
|
||||
|
||||
var sng_manager = main.get_node_or_null("StopNGoManager") if main else null
|
||||
|
||||
# 1. STOP N GO: Reach the finish line if goals are complete
|
||||
if is_sng and sng_manager and sng_manager.is_mission_complete(actor.name.to_int()):
|
||||
# MISSION COMPLETE: We CAN finish, but should we?
|
||||
|
||||
if not is_late_game:
|
||||
# CHAOS MODE: Allow falling through to target holo tiles, but we'll limit the target X later
|
||||
# print("[BotStrategicPlanner] %s mission complete (Chaos Phase %.1fs). Roaming field." % [actor.name, time_left])
|
||||
pass
|
||||
else:
|
||||
# Late game: go to finish
|
||||
var finish_target = Vector2i(21, actor.current_position.y)
|
||||
|
||||
# Ensure finish_target is walkable
|
||||
if not _is_valid_move_target(finish_target):
|
||||
for dy in [1, -1, 2, -2]:
|
||||
var alt = Vector2i(21, actor.current_position.y + dy)
|
||||
if _is_valid_move_target(alt):
|
||||
finish_target = alt
|
||||
break
|
||||
|
||||
print("[BotStrategicPlanner] %s mission complete (Late Game %.1fs)! Heading to finish: %s" % [actor.name, time_left, finish_target])
|
||||
return finish_target
|
||||
|
||||
var is_mission_complete = sng_manager.is_mission_complete(actor.name.to_int()) if sng_manager else false
|
||||
|
||||
var needed_tiles = get_tiles_needed()
|
||||
|
||||
@@ -69,9 +69,9 @@ func generate_guest_name() -> String:
|
||||
_used_guest_names.append(fallback)
|
||||
return fallback
|
||||
|
||||
func release_guest_name(name: String) -> void:
|
||||
func release_guest_name(guest_name: String) -> void:
|
||||
"""Call when a guest disconnects to free up their name for reuse."""
|
||||
_used_guest_names.erase(name)
|
||||
_used_guest_names.erase(guest_name)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Bot name generation
|
||||
@@ -98,9 +98,9 @@ func generate_bot_name() -> String:
|
||||
_used_bot_names.append(candidate)
|
||||
return candidate
|
||||
|
||||
func release_bot_name(name: String) -> void:
|
||||
func release_bot_name(bot_name: String) -> void:
|
||||
"""Call when a bot slot is removed to free up its name."""
|
||||
_used_bot_names.erase(name)
|
||||
_used_bot_names.erase(bot_name)
|
||||
|
||||
func reset_all() -> void:
|
||||
"""Reset both pools — useful on returning to main menu."""
|
||||
|
||||
@@ -173,15 +173,15 @@ func send_lobby_invite(to_user_id: String, match_id: String) -> void:
|
||||
if result.get("success", false) == false:
|
||||
push_warning("[FriendManager] send_lobby_invite failed: " + str(result.get("error", "")))
|
||||
|
||||
func _on_notification_received(notification) -> void:
|
||||
print("[FriendManager] _on_notification_received: code=%d sender=%s" % [notification.code, notification.sender_id])
|
||||
match notification.code:
|
||||
func _on_notification_received(notif) -> void:
|
||||
print("[FriendManager] _on_notification_received: code=%d sender=%s" % [notif.code, notif.sender_id])
|
||||
match notif.code:
|
||||
NOTIF_LOBBY_INVITE:
|
||||
var content = JSON.parse_string(notification.content)
|
||||
var content = JSON.parse_string(notif.content)
|
||||
if content:
|
||||
var from_name: String = content.get("from_name", "Someone")
|
||||
var match_id: String = content.get("match_id", "")
|
||||
emit_signal("lobby_invite_received", notification.sender_id, from_name, match_id)
|
||||
emit_signal("lobby_invite_received", notif.sender_id, from_name, match_id)
|
||||
NOTIF_FRIEND_REQUEST:
|
||||
# Refresh friends list so the incoming request appears immediately
|
||||
print("[FriendManager] friend request notification received, reloading friends...")
|
||||
|
||||
@@ -32,11 +32,6 @@ func pull(banner_id: String, count: int) -> Array:
|
||||
push_error("[GachaManager] Not authenticated")
|
||||
return []
|
||||
|
||||
var payload = JSON.stringify({
|
||||
"banner_id": banner_id,
|
||||
"count": count
|
||||
})
|
||||
|
||||
var result = await BackendService.perform_gacha_pull(banner_id, count)
|
||||
|
||||
if result.get("success", false) == false:
|
||||
|
||||
@@ -44,7 +44,7 @@ func initialize(main: Node):
|
||||
if LobbyManager:
|
||||
LobbyManager.enable_cycle_timer_changed.connect(_on_enable_cycle_timer_changed)
|
||||
|
||||
func _on_enable_cycle_timer_changed(enabled: bool):
|
||||
func _on_enable_cycle_timer_changed(_enabled: bool):
|
||||
# If disabled mid-cycle, the timer will just freeze in _process
|
||||
# If enabled mid-cycle, it will resume
|
||||
pass
|
||||
@@ -69,9 +69,7 @@ func _process(delta):
|
||||
return
|
||||
|
||||
# Skip countdown if timer is disabled
|
||||
var lobby_manager = get_tree().get_root().get_node_or_null("Main/LobbyManager")
|
||||
# Note: LobbyManager is an Autoload, so we can access it directly via 'LobbyManager'
|
||||
|
||||
if LobbyManager and not LobbyManager.get_enable_cycle_timer():
|
||||
# If timer is disabled, we just don't decrement.
|
||||
# But we still keep is_cycle_active = true so the phase is "active" (allowing actions)
|
||||
|
||||
@@ -2,15 +2,12 @@ extends Node
|
||||
|
||||
# PlayerManager - Handles player and bot lifecycle management
|
||||
|
||||
signal player_added(peer_id, player_node)
|
||||
signal player_removed(peer_id)
|
||||
signal bot_created(bot_id, bot_node)
|
||||
signal bot_removed(bot_id)
|
||||
|
||||
var player_scene = preload("res://scenes/player.tscn")
|
||||
var connected_peer_ids = []
|
||||
|
||||
func add_player_character(peer_id: int, position: Vector2i = Vector2i.ZERO) -> Node:
|
||||
func add_player_character(peer_id: int, _position: Vector2i = Vector2i.ZERO) -> Node:
|
||||
var player_character = player_scene.instantiate()
|
||||
player_character.set_multiplayer_authority(peer_id)
|
||||
player_character.name = str(peer_id)
|
||||
|
||||
@@ -282,11 +282,6 @@ func list_matches_async(mode_filter: String = "") -> Array:
|
||||
# Batch-read room metadata from Nakama storage
|
||||
var meta_map: Dictionary = {} # match_id -> {host_name, game_mode, ...}
|
||||
if read_ids.size() > 0:
|
||||
var storage_reads: Array = []
|
||||
for mid in read_ids:
|
||||
# We don't know the owner_id, so list objects by collection+key
|
||||
# Use list_storage_objects per match or batch read
|
||||
pass
|
||||
# Read all match metas from our own storage (other users' objects are public read)
|
||||
for mid in read_ids:
|
||||
var list_result = await client.list_storage_objects_async(session, "match_meta", "", 100)
|
||||
|
||||
@@ -87,13 +87,13 @@ func get_steamworks_manager() -> Node:
|
||||
return steamworks_manager
|
||||
|
||||
## Unified RPC with Retry and Error Mapping
|
||||
func api_rpc_async(rpc_id: String, payload: String = "{}") -> Dictionary:
|
||||
func api_rpc_async(p_rpc_id: String, payload: String = "{}") -> Dictionary:
|
||||
if not nakama_backend or not nakama_backend.client or not nakama_backend.session:
|
||||
return { "success": false, "error": ErrorCode.UNAUTHORIZED, "message": "Not authenticated" }
|
||||
|
||||
var retries := 0
|
||||
while retries <= MAX_RETRIES:
|
||||
var result = await nakama_backend.client.rpc_async(nakama_backend.session, rpc_id, payload)
|
||||
var result = await nakama_backend.client.rpc_async(nakama_backend.session, p_rpc_id, payload)
|
||||
|
||||
# NakamaAPI.ApiRpc has is_exception()
|
||||
if result.is_exception():
|
||||
|
||||
@@ -95,7 +95,7 @@ func _on_update_pressed() -> void:
|
||||
status_label.text = "Downloading update..."
|
||||
update_manager.download_update()
|
||||
|
||||
func _on_download_started(total_size: int) -> void:
|
||||
func _on_download_started(_total_size: int) -> void:
|
||||
progress_bar.visible = true
|
||||
progress_label.visible = true
|
||||
progress_bar.value = 0
|
||||
|
||||
Reference in New Issue
Block a user