bugfix, desync, and add UI function
This commit is contained in:
@@ -33,6 +33,11 @@ func _ready():
|
||||
rng.seed = name.hash()
|
||||
_tick_counter = rng.randi() % tick_rate
|
||||
|
||||
# Mobile Optimization: Throttling
|
||||
if OS.has_feature("mobile") or OS.has_feature("android") or OS.has_feature("ios"):
|
||||
tick_rate = int(tick_rate * 1.5) # 50% slower updates on mobile
|
||||
print("[BotController] Mobile detected! Throttling tick rate to: ", tick_rate)
|
||||
|
||||
# Get parent (should be player character)
|
||||
actor = get_parent()
|
||||
# ... (rest of _ready) ...
|
||||
@@ -153,7 +158,13 @@ func _run_ai_tick():
|
||||
print("[BotController] Action Taken: Arrange")
|
||||
return
|
||||
|
||||
print("[BotController] No action taken (Idle). AP: %d, GoalsAchieved: %s" % [actor.action_points, _is_goals_achieved()])
|
||||
print("[BotController] %s - No action taken (Idle). AP: %d, GoalsAchieved: %s" % [actor.name, actor.action_points, _is_goals_achieved()])
|
||||
|
||||
# STALL PREVENTION: If we have AP but couldn't do anything, we are stuck.
|
||||
# Skip turn to prevent game freeze in turn-based mode.
|
||||
if TurnManager.turn_based_mode and actor.action_points > 0:
|
||||
print("[BotController] %s is STUCK with AP %d! Skipping turn to proceed flow." % [actor.name, actor.action_points])
|
||||
actor.consume_action_points(actor.action_points)
|
||||
|
||||
# =============================================================================
|
||||
# Power-Up / Sabotage
|
||||
|
||||
@@ -223,27 +223,64 @@ func find_best_tile_to_grab() -> Dictionary:
|
||||
return best_tile
|
||||
|
||||
func find_nearest_tile_of_type(tile_types: Array) -> Vector2i:
|
||||
"""Find nearest tile matching any type in array."""
|
||||
"""Find nearest tile matching any type in array using optimized spiral search."""
|
||||
var current_pos = actor.current_position
|
||||
var nearest_pos = Vector2i(-1, -1)
|
||||
var nearest_dist = 999999
|
||||
|
||||
if not enhanced_gridmap:
|
||||
return nearest_pos
|
||||
return Vector2i(-1, -1)
|
||||
|
||||
for x in range(enhanced_gridmap.columns):
|
||||
for z in range(enhanced_gridmap.rows):
|
||||
var pos = Vector2i(x, z)
|
||||
var cell = Vector3i(x, 1, z)
|
||||
var item = enhanced_gridmap.get_cell_item(cell)
|
||||
# Optimization: Start check at simple radius
|
||||
# If we find something in the spiral, it is guaranteed to be one of the nearest (by Chebyshev distance logic broadly, or just good enough)
|
||||
|
||||
var max_radius = 25 # Limit search range to prevent full map scans on huge maps
|
||||
if OS.has_feature("mobile"):
|
||||
max_radius = 15 # Stricter limit on mobile
|
||||
|
||||
# Check center first
|
||||
var center_cell = Vector3i(current_pos.x, 1, current_pos.y)
|
||||
if enhanced_gridmap.get_cell_item(center_cell) in tile_types:
|
||||
return current_pos
|
||||
|
||||
for r in range(1, max_radius + 1):
|
||||
# Spiral perimeter:
|
||||
# Top row: (x-r, y-r) to (x+r, y-r)
|
||||
# Bottom row: (x-r, y+r) to (x+r, y+r)
|
||||
# Left col: (x-r, y-r+1) to (x-r, y+r-1)
|
||||
# Right col: (x+r, y-r+1) to (x+r, y+r-1)
|
||||
var found_in_layer = []
|
||||
|
||||
# We'll check the ring. Note: Manhattan distance might be better metric for "nearest"
|
||||
# but layer-by-layer is efficient for finding "close enough" quickly.
|
||||
|
||||
for x_off in range(-r, r + 1):
|
||||
_check_spiral_cell(current_pos.x + x_off, current_pos.y - r, tile_types, found_in_layer) # Top
|
||||
_check_spiral_cell(current_pos.x + x_off, current_pos.y + r, tile_types, found_in_layer) # Bottom
|
||||
|
||||
if item in tile_types:
|
||||
for y_off in range(-r + 1, r):
|
||||
_check_spiral_cell(current_pos.x - r, current_pos.y + y_off, tile_types, found_in_layer) # Left
|
||||
_check_spiral_cell(current_pos.x + r, current_pos.y + y_off, tile_types, found_in_layer) # Right
|
||||
|
||||
if found_in_layer.size() > 0:
|
||||
# If we found candidates in this layer, pick the physically closest one (Euclidean/Manhattan refinement)
|
||||
var nearest_in_layer = found_in_layer[0]
|
||||
var min_dist = 999999
|
||||
for pos in found_in_layer:
|
||||
var dist = abs(pos.x - current_pos.x) + abs(pos.y - current_pos.y)
|
||||
if dist < nearest_dist:
|
||||
nearest_dist = dist
|
||||
nearest_pos = pos
|
||||
if dist < min_dist:
|
||||
min_dist = dist
|
||||
nearest_in_layer = pos
|
||||
return nearest_in_layer
|
||||
|
||||
return nearest_pos
|
||||
return Vector2i(-1, -1)
|
||||
|
||||
func _check_spiral_cell(x: int, z: int, tile_types: Array, result_array: Array):
|
||||
if x < 0 or z < 0 or x >= enhanced_gridmap.columns or z >= enhanced_gridmap.rows:
|
||||
return
|
||||
|
||||
var cell = Vector3i(x, 1, z)
|
||||
var item = enhanced_gridmap.get_cell_item(cell)
|
||||
if item in tile_types:
|
||||
result_array.append(Vector2i(x, z))
|
||||
|
||||
# =============================================================================
|
||||
# Movement Strategy
|
||||
|
||||
@@ -64,7 +64,7 @@ func _try_restore_session() -> void:
|
||||
if session.is_expired():
|
||||
# Try to refresh
|
||||
if refresh_token:
|
||||
var refreshed := await NakamaManager.client.session_refresh_async(session)
|
||||
var refreshed: NakamaSession = await NakamaManager.client.session_refresh_async(session)
|
||||
if not refreshed.is_exception():
|
||||
session = refreshed
|
||||
_save_session(session, saved_auth_mode)
|
||||
@@ -118,7 +118,7 @@ func login_as_guest() -> bool:
|
||||
var device_id := _get_device_id()
|
||||
print("[AuthManager] Guest login with device: ", device_id.substr(0, 8) + "...")
|
||||
|
||||
var session := await NakamaManager.client.authenticate_device_async(device_id, null, true)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_device_async(device_id, null, true)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -174,7 +174,7 @@ func login_with_email(email: String, password: String, remember: bool = true) ->
|
||||
|
||||
print("[AuthManager] Email login: ", email)
|
||||
|
||||
var session := await NakamaManager.client.authenticate_email_async(email, password, null, false)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_email_async(email, password, null, false)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -205,7 +205,7 @@ func register_with_email(email: String, password: String, username: String = "")
|
||||
print("[AuthManager] Registering: ", email)
|
||||
|
||||
# Create account (true = create if not exists)
|
||||
var session := await NakamaManager.client.authenticate_email_async(email, password, username, true)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_email_async(email, password, username, true)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -237,7 +237,7 @@ func login_with_google(id_token: String) -> bool:
|
||||
|
||||
print("[AuthManager] Google login...")
|
||||
|
||||
var session := await NakamaManager.client.authenticate_google_async(id_token, null, true)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_google_async(id_token, null, true)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -265,7 +265,7 @@ func login_with_apple(id_token: String) -> bool:
|
||||
|
||||
print("[AuthManager] Apple login...")
|
||||
|
||||
var session := await NakamaManager.client.authenticate_apple_async(id_token, null, true)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_apple_async(id_token, null, true)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -293,7 +293,7 @@ func login_with_facebook(access_token: String) -> bool:
|
||||
|
||||
print("[AuthManager] Facebook login...")
|
||||
|
||||
var session := await NakamaManager.client.authenticate_facebook_async(access_token, null, true)
|
||||
var session: NakamaSession = await NakamaManager.client.authenticate_facebook_async(access_token, null, true)
|
||||
|
||||
if session.is_exception():
|
||||
var error: String = session.get_exception().message
|
||||
@@ -324,7 +324,7 @@ func link_email(email: String, password: String) -> bool:
|
||||
|
||||
print("[AuthManager] Linking email to guest account...")
|
||||
|
||||
var result := await NakamaManager.client.link_email_async(NakamaManager.session, email, password)
|
||||
var result: NakamaAsyncResult = await NakamaManager.client.link_email_async(NakamaManager.session, email, password)
|
||||
|
||||
if result.is_exception():
|
||||
push_error("[AuthManager] Link failed: " + result.get_exception().message)
|
||||
@@ -341,7 +341,7 @@ func link_google(id_token: String) -> bool:
|
||||
if not is_authenticated or not NakamaManager.session:
|
||||
return false
|
||||
|
||||
var result := await NakamaManager.client.link_google_async(NakamaManager.session, id_token)
|
||||
var result: NakamaAsyncResult = await NakamaManager.client.link_google_async(NakamaManager.session, id_token)
|
||||
|
||||
if result.is_exception():
|
||||
return false
|
||||
|
||||
@@ -31,7 +31,7 @@ var match_duration: int = 180 # Default 3 minutes
|
||||
var randomize_spawn: bool = true # Default enabled
|
||||
|
||||
# Timer setting
|
||||
var enable_cycle_timer: bool = true # Default enabled
|
||||
var enable_cycle_timer: bool = false # Default disabled
|
||||
signal enable_cycle_timer_changed(enabled: bool)
|
||||
|
||||
# Character and area selection
|
||||
@@ -419,4 +419,4 @@ func reset() -> void:
|
||||
match_duration = 180 # Reset to default 3 minutes
|
||||
selected_area = "Desert"
|
||||
local_character_index = 0
|
||||
enable_cycle_timer = true
|
||||
enable_cycle_timer = false
|
||||
|
||||
@@ -40,77 +40,108 @@ func set_player(p_player: Node3D):
|
||||
local_player = p_player
|
||||
|
||||
func _create_touch_ui():
|
||||
print("[TouchControls] Creating touch UI...")
|
||||
print("[TouchControls] Creating/Finding touch UI...")
|
||||
# Use layer 10 - above regular UI but below pause menu
|
||||
layer = 10
|
||||
|
||||
# Create main container
|
||||
var container = Control.new()
|
||||
container.name = "TouchControls"
|
||||
container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
container.mouse_filter = Control.MOUSE_FILTER_PASS # Pass input to children
|
||||
add_child(container)
|
||||
# Check if container already exists (added in scene)
|
||||
var container = get_node_or_null("TouchControls")
|
||||
|
||||
# Create virtual joystick (bottom-left)
|
||||
var joystick_script = load("res://scripts/ui/virtual_joystick.gd")
|
||||
virtual_joystick = Control.new()
|
||||
virtual_joystick.set_script(joystick_script)
|
||||
virtual_joystick.name = "VirtualJoystick"
|
||||
virtual_joystick.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
||||
if not container:
|
||||
# Create main container if missing
|
||||
container = Control.new()
|
||||
container.name = "TouchControls"
|
||||
container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
container.mouse_filter = Control.MOUSE_FILTER_PASS # Pass input to children
|
||||
add_child(container)
|
||||
else:
|
||||
print("[TouchControls] Found existing TouchControls container")
|
||||
|
||||
# Helper to find or create control
|
||||
var find_or_create_joystick = func():
|
||||
var joy = container.get_node_or_null("VirtualJoystick")
|
||||
if joy:
|
||||
print("[TouchControls] Found existing VirtualJoystick")
|
||||
return joy
|
||||
|
||||
var joystick_script = load("res://scripts/ui/virtual_joystick.gd")
|
||||
joy = Control.new()
|
||||
joy.set_script(joystick_script)
|
||||
joy.name = "VirtualJoystick"
|
||||
joy.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
||||
|
||||
# Use standard size from joystick script defaults (radius 60 -> size 160)
|
||||
var joy_size = Vector2(160, 160)
|
||||
joy.custom_minimum_size = joy_size
|
||||
joy.size = joy_size
|
||||
|
||||
joy.offset_left = 120
|
||||
joy.offset_top = -280
|
||||
joy.offset_right = 280
|
||||
joy.offset_bottom = -120
|
||||
|
||||
container.add_child(joy)
|
||||
return joy
|
||||
|
||||
virtual_joystick = find_or_create_joystick.call()
|
||||
if not virtual_joystick.direction_changed.is_connected(_on_joystick_direction):
|
||||
virtual_joystick.direction_changed.connect(_on_joystick_direction)
|
||||
|
||||
# Use standard size from joystick script defaults (radius 60 -> size 160)
|
||||
var joy_size = Vector2(160, 160)
|
||||
virtual_joystick.custom_minimum_size = joy_size
|
||||
virtual_joystick.size = joy_size
|
||||
|
||||
# Position relative to Bottom-Left anchor
|
||||
# joystick_position (120, -120) interpreted as margin from anchor
|
||||
# x=120 (right from left edge), y=-120 (up from bottom edge - implies bottom margin)
|
||||
# We want the *center* or *bottom-left* corner?
|
||||
# Assuming (120, -120) is top-left corner of the control relative to anchor?
|
||||
# Let's align bottom-left corner of control to (120, -120) from screen bottom-left
|
||||
# Screen Bottom-Left is (0, 1) in normalized anchors.
|
||||
# offset_left = 120
|
||||
# offset_bottom = -120 (120px up from bottom)
|
||||
# offset_top = -120 - 160 = -280
|
||||
# offset_right = 120 + 160 = 280
|
||||
|
||||
virtual_joystick.offset_left = 120
|
||||
virtual_joystick.offset_top = -280
|
||||
virtual_joystick.offset_right = 280
|
||||
virtual_joystick.offset_bottom = -120
|
||||
|
||||
virtual_joystick.direction_changed.connect(_on_joystick_direction)
|
||||
container.add_child(virtual_joystick)
|
||||
# Helper to find or create button logic moved to function _find_or_create_action_button
|
||||
|
||||
# Create action buttons (bottom-right)
|
||||
grab_button = _create_action_button("Grab", "👋", button_positions.grab)
|
||||
put_button = _create_action_button("Put", "📦", button_positions.put)
|
||||
special_button = _create_action_button("Special", "⚡", button_positions.special)
|
||||
|
||||
container.add_child(grab_button)
|
||||
container.add_child(put_button)
|
||||
container.add_child(special_button)
|
||||
grab_button = _find_or_create_action_button(container, "Grab", "👋", button_positions.grab)
|
||||
put_button = _find_or_create_action_button(container, "Put", "📦", button_positions.put)
|
||||
special_button = _find_or_create_action_button(container, "Special", "⚡", button_positions.special)
|
||||
|
||||
# Create settings button (top-right corner)
|
||||
settings_button = Button.new()
|
||||
settings_button.name = "SettingsBtn"
|
||||
settings_button.text = "⚙"
|
||||
settings_button.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||||
settings_button.offset_left = -70 # Use offsets instead of position for anchored controls
|
||||
settings_button.offset_right = -20
|
||||
settings_button.offset_top = 70
|
||||
settings_button.offset_bottom = 120
|
||||
settings_button.custom_minimum_size = Vector2(50, 50)
|
||||
settings_button.mouse_filter = Control.MOUSE_FILTER_STOP # Ensure it receives input
|
||||
settings_button.pressed.connect(_on_settings_pressed)
|
||||
_style_button(settings_button, 0.5)
|
||||
container.add_child(settings_button)
|
||||
settings_button = container.get_node_or_null("SettingsBtn")
|
||||
if not settings_button:
|
||||
settings_button = Button.new()
|
||||
settings_button.name = "SettingsBtn"
|
||||
settings_button.text = "⚙"
|
||||
settings_button.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||||
settings_button.offset_left = -70
|
||||
settings_button.offset_right = -20
|
||||
settings_button.offset_top = 70
|
||||
settings_button.offset_bottom = 120
|
||||
settings_button.custom_minimum_size = Vector2(50, 50)
|
||||
settings_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_style_button(settings_button, 0.5)
|
||||
container.add_child(settings_button)
|
||||
|
||||
if not settings_button.pressed.is_connected(_on_settings_pressed):
|
||||
settings_button.pressed.connect(_on_settings_pressed)
|
||||
|
||||
# Always visible now - controlled by settings toggle
|
||||
# Can be hidden via settings if user doesn't want touch controls on desktop
|
||||
visible = true
|
||||
|
||||
func _find_or_create_action_button(container: Control, button_name: String, icon: String, pos: Vector2) -> Button:
|
||||
var btn = container.get_node_or_null(button_name + "Btn")
|
||||
if btn:
|
||||
print("[TouchControls] Found existing %s button" % button_name)
|
||||
# Style it and connect
|
||||
_style_button(btn, button_opacity)
|
||||
# Avoid duplicate signal connections
|
||||
if not btn.button_down.is_connected(_on_button_pressed): # Wait, cannot check lambda easily
|
||||
# Disconnect all to be safe if previously connected
|
||||
for conn in btn.button_down.get_connections():
|
||||
if conn["callable"].get_object() == self:
|
||||
btn.button_down.disconnect(conn["callable"])
|
||||
for conn in btn.button_up.get_connections():
|
||||
if conn["callable"].get_object() == self:
|
||||
btn.button_up.disconnect(conn["callable"])
|
||||
|
||||
btn.button_down.connect(func(): _on_button_pressed(button_name))
|
||||
btn.button_up.connect(func(): _on_button_released(button_name))
|
||||
return btn
|
||||
|
||||
# Create new
|
||||
var new_btn = _create_action_button(button_name, icon, pos)
|
||||
container.add_child(new_btn)
|
||||
return new_btn
|
||||
|
||||
func _create_action_button(button_name: String, icon: String, pos: Vector2) -> Button:
|
||||
var btn = Button.new()
|
||||
btn.name = button_name + "Btn"
|
||||
|
||||
@@ -91,7 +91,7 @@ func load_stats() -> Dictionary:
|
||||
if not NakamaManager.session:
|
||||
return {}
|
||||
|
||||
var user_id := NakamaManager.session.user_id
|
||||
var user_id: String = NakamaManager.session.user_id
|
||||
|
||||
var storage_result = await NakamaManager.client.read_storage_objects_async(
|
||||
NakamaManager.session,
|
||||
@@ -132,7 +132,7 @@ func update_display_name(new_name: String) -> bool:
|
||||
emit_signal("profile_update_failed", "Display name too long (max 50 characters)")
|
||||
return false
|
||||
|
||||
var result := await NakamaManager.client.update_account_async(
|
||||
var result: NakamaAsyncResult = await NakamaManager.client.update_account_async(
|
||||
NakamaManager.session,
|
||||
null, # username (don't change)
|
||||
new_name # display_name
|
||||
|
||||
Reference in New Issue
Block a user