diff --git a/scenes/main.gd b/scenes/main.gd index d494944..6d36558 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -807,7 +807,7 @@ func _assign_random_spawn_positions(): print("Assigned spawn %s to player %s" % [assigned_pos, player.name]) func _assign_stop_n_go_spawn_positions(all_players: Array): - """Assigns random spawns on walkable arena tiles for Stop N Go mode.""" + """Assigns spawns on column 0, starting from row 1 for Stop N Go mode.""" # Sort players for deterministic assignment based on ID all_players.sort_custom(func(a, b): return a.name.to_int() < b.name.to_int()) @@ -815,33 +815,12 @@ func _assign_stop_n_go_spawn_positions(all_players: Array): if not enhanced_gridmap: return - # Collect all valid walkable positions (not obstacle, not void) - var valid_positions: Array[Vector2i] = [] - for x in range(enhanced_gridmap.columns): - for z in range(enhanced_gridmap.rows): - var tile = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) - # Accept walkable (0), start (3), finish (3) — skip obstacles (4) and void (-1) - if tile != -1 and tile != 4: - valid_positions.append(Vector2i(x, z)) - - valid_positions.shuffle() - - var used_positions: Array[Vector2i] = [] + var col = 0 + var current_row = 1 for player in all_players: - var assigned_pos = Vector2i(-1, -1) - - # Find a random walkable position not occupied by another player - for pos in valid_positions: - if pos not in used_positions: - assigned_pos = pos - break - - if assigned_pos == Vector2i(-1, -1): - # Fallback: center of arena - assigned_pos = Vector2i(int(enhanced_gridmap.columns / 2), int(enhanced_gridmap.rows / 2)) - - used_positions.append(assigned_pos) + var assigned_pos = Vector2i(col, current_row) + current_row += 1 # Ensure immediate sync player.position = player.grid_to_world(assigned_pos) @@ -851,7 +830,7 @@ func _assign_stop_n_go_spawn_positions(all_players: Array): if can_rpc(): player.rpc("set_spawn_position", assigned_pos) - print("[StopNGo] Assigned random spawn %s to player %s" % [assigned_pos, player.name]) + print("[StopNGo] Assigned spawn %s to player %s" % [assigned_pos, player.name]) func _assign_portal_mode_spawn_positions(all_players: Array): """Assigns spawns to different quadrants for Tekton Doors mode.""" @@ -1584,14 +1563,23 @@ func randomize_item_at_position(grid_position: Vector2i): if is_ground: var new_item = 7 - # Use ScarcityController - new_item = ScarcityController.get_random_tile_id() + + var get_mode_specific_tile = func(): + if LobbyManager.game_mode != "Stop n Go" and LobbyManager.game_mode != "Tekton Doors": + # 60% Chance for Common (7-10), 40% for PowerUp + if randf() <= 0.6: + return [7, 8, 9, 10].pick_random() + else: + return ScarcityModel.SPECIAL_TILES.pick_random() + return ScarcityController.get_random_tile_id() + + new_item = get_mode_specific_tile.call() # If we are replacing an existing item, try to ensure it changes if current_item != -1: var max_retries = 3 while new_item == current_item and max_retries > 0: - new_item = ScarcityController.get_random_tile_id() + new_item = get_mode_specific_tile.call() max_retries -= 1 sync_grid_item(cell.x, cell.y, cell.z, new_item) @@ -1654,10 +1642,12 @@ func randomize_game_grid(): var enhanced_gridmap = $EnhancedGridMap if enhanced_gridmap: - # Use density-aware callable: 60% chance for a real tile, 40% for none + # Custom spawn ratio for Free Mode: 60% common tiles, 40% empty tiles (start of game) var density_callable = func(): - if randf() > 0.6: return -1 - return ScarcityController.get_random_tile_id() + if randf() <= 0.6: + return ScarcityModel.STANDARD_TILES.pick_random() + else: + return -1 # Empty enhanced_gridmap.randomize_floor(1, density_callable) diff --git a/scenes/player.gd b/scenes/player.gd index 8a1e373..7250842 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -2084,6 +2084,11 @@ func sync_grab_tekton(tekton_path: NodePath): carried_tekton = tekton is_carrying_tekton = true tekton.set_carried(true, self ) + + # Disposed of AttackMode upon grab + if is_attack_mode: + is_attack_mode = false + print("[Player %s] Grabbed Tekton %s" % [name, tekton.name]) func throw_tekton(): diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index 1ef18cb..e001017 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -125,8 +125,9 @@ func handle_unhandled_input(event): if player.powerup_manager.has_method("spawn_boost_reward"): player.powerup_manager.spawn_boost_reward() KEY_G: - if not player.is_carrying_tekton: - player.grab_tekton() + if not player.is_carrying_tekton and player.powerup_manager: + if player.powerup_manager.can_use_special(): + player.grab_tekton() # Handle spawn point selection if not yet selected diff --git a/scripts/managers/powerup_manager.gd b/scripts/managers/powerup_manager.gd index 2d0b3b0..329c09c 100644 --- a/scripts/managers/powerup_manager.gd +++ b/scripts/managers/powerup_manager.gd @@ -156,6 +156,11 @@ func use_special_effect() -> bool: if not can_use_special(): return false + # Restriction: Cannot use attack mode while carrying a Tekton + if player.is_carrying_tekton: + NotificationManager.send_message(player, "Cannot enter Attack Mode while carrying a Tekton!", NotificationManager.MessageType.WARNING) + return false + # Enable Attack Mode explicitly player.is_attack_mode = true # Do NOT consume boost here. Boost acts as "fuel" for the attacks. @@ -183,14 +188,15 @@ func spawn_boost_reward() -> bool: return false if player.special_tiles_manager and player.special_tiles_manager.has_method("spawn_powerups_around"): - # Spawn only common tiles (7-10) with 100% density - player.special_tiles_manager.spawn_powerups_around(player.current_position, true, true, true) + # Spawn tiles (Stop N Go gets only common, Free Mode gets 60/40 ratio) + player.special_tiles_manager.spawn_powerups_around(player.current_position, true, false, true) # Drop the Tekton after spawning if player.has_method("drop_tekton"): player.drop_tekton() - print("[PowerUp] %s used Tekton to SPAWN TILES (100%% Density Common)." % player.name) + reset_boost() + print("[PowerUp] %s used Tekton to SPAWN TILES and consumed Boost." % player.name) return true return false diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index ac43bb5..b933579 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -441,14 +441,14 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true, only_c var mode = LobbyManager.get_game_mode() if only_common or LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO): - # Spawn ONLY goals (7-10) for Stop n Go or if forced + # Spawn ONLY common tiles (7-10) for Stop n Go or if forced item_id = rng.randi_range(7, 10) else: - # Free mode: 60% Chance for Normal Tile (7-10), 40% for PowerUp + # Free mode: 60% Chance for Common Tile (7-10), 40% for PowerUp if rng.randf() < 0.6: item_id = rng.randi_range(7, 10) else: - # 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes) + # 40% Chance for PowerUp var is_restricted = GameMode.is_restricted(mode) if is_restricted: item_id = [11, 14].pick_random() diff --git a/scripts/managers/touch_controls.gd b/scripts/managers/touch_controls.gd index 09db2d7..59fdadd 100644 --- a/scripts/managers/touch_controls.gd +++ b/scripts/managers/touch_controls.gd @@ -524,13 +524,17 @@ func _on_boost_points_changed(current_points: int, max_points: int): # User Request: Disable Special & SpawnBoost if < 100% var is_full = current_points >= (max_points - 1) # Tolerance - _update_boost_button_state(attack_mode_button, is_full) + # Attack Mode (⚡) is only enabled if full AND not carrying a Tekton + var can_attack = is_full and not (local_player and local_player.is_carrying_tekton) + _update_boost_button_state(attack_mode_button, can_attack) # SpawnBoost depends on carrying a Tekton, not boost points var can_spawn = local_player and local_player.is_carrying_tekton _update_boost_button_state(spawn_boost_button, can_spawn) - _update_boost_button_state(tekton_grab_button, true) # Always enabled (logic handles grab-ability) + # Tekton Grab (👋) is only enabled if full AND not already carrying one + var can_grab = is_full and not (local_player and local_player.is_carrying_tekton) + _update_boost_button_state(tekton_grab_button, can_grab) func _on_tekton_carried_changed(_is_carrying: bool): # Refresh button states when player grabs/throws a tekton diff --git a/scripts/tekton.gd b/scripts/tekton.gd index 82a38b9..780a906 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -387,12 +387,9 @@ func spawn_tiles_around(count: int = 4): if roll < 0.6: # 60% Normal Tile (7-10) item_id = rng.randi_range(7, 10) - elif roll < 0.9: - # 30% PowerUp (11-14) - item_id = rng.randi_range(11, 14) else: - # 10% Obstacle/Trap (optional) - item_id = -1 # Clear? + # 40% PowerUp (11-14) + item_id = rng.randi_range(11, 14) if item_id != -1: var main = get_tree().get_root().get_node_or_null("Main")