feat: Introduce an EnhancedGridMap with advanced generation, randomization, pathfinding, and data serialization, along with new player, powerup, and portal managers.
This commit is contained in:
@@ -16,7 +16,7 @@ signal grid_updated
|
|||||||
@export var hover_item: int = 1
|
@export var hover_item: int = 1
|
||||||
@export var start_item: int = 2
|
@export var start_item: int = 2
|
||||||
@export var end_item: int = 3
|
@export var end_item: int = 3
|
||||||
@export var immutable_items: Array[int] = [4] # Items that cannot be randomized/reset
|
@export var immutable_items: Array[int] = [1, 2, 3, 4] # Items that cannot be randomized/reset (Start, Safe, Finish, Wall)
|
||||||
|
|
||||||
var current_mesh_library: MeshLibrary
|
var current_mesh_library: MeshLibrary
|
||||||
var grid_data: Array = [] # 3D array [floor][row][column]
|
var grid_data: Array = [] # 3D array [floor][row][column]
|
||||||
|
|||||||
+16
-13
@@ -1581,12 +1581,15 @@ func request_randomize_item(grid_position: Vector2i):
|
|||||||
func sync_grid_item(x: int, y: int, z: int, item: int):
|
func sync_grid_item(x: int, y: int, z: int, item: int):
|
||||||
var enhanced_gridmap = $EnhancedGridMap
|
var enhanced_gridmap = $EnhancedGridMap
|
||||||
if enhanced_gridmap:
|
if enhanced_gridmap:
|
||||||
# WALL-SAFETY CHECK: Block tiles (7-20) from being placed on walls (4) or void (-1)
|
# PROTECTED FLOOR CHECK: Block tiles (7-20) from being placed on walls (4) or void (-1)
|
||||||
|
# Note: We allow spawning on Safe Zones, Start, and Finish as it's on Layer 1.
|
||||||
if y == 1 and item >= 7 and item <= 20:
|
if y == 1 and item >= 7 and item <= 20:
|
||||||
var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||||
if f0 == 4 or f0 == -1:
|
var f1 = enhanced_gridmap.get_cell_item(Vector3i(x, 1, z))
|
||||||
# Log and block illegal placement
|
|
||||||
print("[Main] Blocked illegal tile (%d) placement on wall/void at (%d, %d)" % [item, x, z])
|
# Block if Layer 0 is Wall (4) or Void (-1)
|
||||||
|
# OR Layer 1 is already a wall (4 or 13)
|
||||||
|
if f0 in [4, -1] or f1 == 4 or f1 == 13:
|
||||||
return
|
return
|
||||||
|
|
||||||
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
||||||
@@ -1607,10 +1610,11 @@ func sync_grid_items_batch(data: Array):
|
|||||||
var z = entry.get("z", 0)
|
var z = entry.get("z", 0)
|
||||||
var item = entry.get("item", -1)
|
var item = entry.get("item", -1)
|
||||||
|
|
||||||
# WALL-SAFETY CHECK
|
# PROTECTED FLOOR CHECK
|
||||||
if y == 1 and item >= 7 and item <= 20:
|
if y == 1 and item >= 7 and item <= 20:
|
||||||
var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||||
if f0 == 4 or f0 == -1:
|
var f1 = enhanced_gridmap.get_cell_item(Vector3i(x, 1, z))
|
||||||
|
if f0 in [4, -1] or f1 == 4 or f1 == 13:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
||||||
@@ -1625,13 +1629,12 @@ func randomize_game_grid():
|
|||||||
|
|
||||||
var enhanced_gridmap = $EnhancedGridMap
|
var enhanced_gridmap = $EnhancedGridMap
|
||||||
if enhanced_gridmap:
|
if enhanced_gridmap:
|
||||||
# Randomize Floor 1 using ScarcityController
|
# Use density-aware callable: 60% chance for a real tile, 40% for none
|
||||||
enhanced_gridmap.randomize_floor(1, ScarcityController.get_random_tile_id)
|
var density_callable = func():
|
||||||
|
if randf() > 0.6: return -1
|
||||||
# Sync to clients if needed (usually handled by initial state sync or explicit item syncs)
|
return ScarcityController.get_random_tile_id()
|
||||||
# Since Main.gd doesn't have a "Sync Floor" RPC, we rely on clients running the same seed or syncing individual cells.
|
|
||||||
# For now, let's assume server authority + sync on connect handles it, or add sync loop if critical.
|
enhanced_gridmap.randomize_floor(1, density_callable)
|
||||||
pass
|
|
||||||
|
|
||||||
@rpc("authority", "call_local", "reliable")
|
@rpc("authority", "call_local", "reliable")
|
||||||
func sync_full_grid_data_stop_n_go(floor0: PackedInt32Array, floor1: PackedInt32Array, cols: int, rows: int):
|
func sync_full_grid_data_stop_n_go(floor0: PackedInt32Array, floor1: PackedInt32Array, cols: int, rows: int):
|
||||||
|
|||||||
+2
-11
@@ -32,7 +32,6 @@
|
|||||||
[ext_resource type="Texture2D" uid="uid://3up2su2e0lfa" path="res://assets/graphics/touch_control/freeze_area.png" id="28_fv21b"]
|
[ext_resource type="Texture2D" uid="uid://3up2su2e0lfa" path="res://assets/graphics/touch_control/freeze_area.png" id="28_fv21b"]
|
||||||
[ext_resource type="Texture2D" uid="uid://ckhdyxnho6sjp" path="res://assets/graphics/touch_control/spawn_tile.png" id="28_j8jky"]
|
[ext_resource type="Texture2D" uid="uid://ckhdyxnho6sjp" path="res://assets/graphics/touch_control/spawn_tile.png" id="28_j8jky"]
|
||||||
[ext_resource type="Texture2D" uid="uid://b2vhatfmufn3d" path="res://assets/graphics/touch_control/ghost.png" id="33_5q0nq"]
|
[ext_resource type="Texture2D" uid="uid://b2vhatfmufn3d" path="res://assets/graphics/touch_control/ghost.png" id="33_5q0nq"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cdwk17moidkj2" path="res://assets/graphics/touch_control/knock_tekton.png" id="35_fuf3a"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://biun2yvglxgij" path="res://assets/graphics/touch_control/grab_tekton.png" id="36_pibwh"]
|
[ext_resource type="Texture2D" uid="uid://biun2yvglxgij" path="res://assets/graphics/touch_control/grab_tekton.png" id="36_pibwh"]
|
||||||
[ext_resource type="Script" uid="uid://86ikh0wuqk7v" path="res://scripts/ui/powerup_inventory_ui.gd" id="powerup_ui_script"]
|
[ext_resource type="Script" uid="uid://86ikh0wuqk7v" path="res://scripts/ui/powerup_inventory_ui.gd" id="powerup_ui_script"]
|
||||||
[ext_resource type="Script" uid="uid://b54tfa0n6kogi" path="res://scripts/managers/touch_controls.gd" id="touch_manager"]
|
[ext_resource type="Script" uid="uid://b54tfa0n6kogi" path="res://scripts/managers/touch_controls.gd" id="touch_manager"]
|
||||||
@@ -99,6 +98,7 @@ columns = 14
|
|||||||
rows = 14
|
rows = 14
|
||||||
floors = 2
|
floors = 2
|
||||||
auto_randomize = true
|
auto_randomize = true
|
||||||
|
immutable_items = Array[int]([4])
|
||||||
metadata/_editor_floor_ = Vector3(0, 1, 0)
|
metadata/_editor_floor_ = Vector3(0, 1, 0)
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="." unique_id=1200003163]
|
[node name="Camera3D" type="Camera3D" parent="." unique_id=1200003163]
|
||||||
@@ -9808,16 +9808,7 @@ flat = true
|
|||||||
icon_alignment = 1
|
icon_alignment = 1
|
||||||
expand_icon = true
|
expand_icon = true
|
||||||
|
|
||||||
[node name="TektonKnockBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2133168886]
|
[node name="TektonGrabBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2097928368]
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
icon = ExtResource("35_fuf3a")
|
|
||||||
flat = true
|
|
||||||
icon_alignment = 1
|
|
||||||
expand_icon = true
|
|
||||||
|
|
||||||
[node name="TektonThrowBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2097928368]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|||||||
@@ -707,6 +707,9 @@ var immunity_timer: float = 0.0
|
|||||||
|
|
||||||
@rpc("any_peer", "call_local")
|
@rpc("any_peer", "call_local")
|
||||||
func apply_stagger(duration: float = 1.5):
|
func apply_stagger(duration: float = 1.5):
|
||||||
|
if is_carrying_tekton:
|
||||||
|
return # Cannot be staggered while carrying a Tekton
|
||||||
|
|
||||||
if immunity_timer > 0:
|
if immunity_timer > 0:
|
||||||
return # Immune!
|
return # Immune!
|
||||||
|
|
||||||
@@ -2191,6 +2194,34 @@ func sync_throw_tekton(target_pos: Vector2i):
|
|||||||
|
|
||||||
print("[Player %s] Threw Tekton to %s (Dist: %s)" % [name, target_pos, target_pos.distance_to(tekton.current_position)])
|
print("[Player %s] Threw Tekton to %s (Dist: %s)" % [name, target_pos, target_pos.distance_to(tekton.current_position)])
|
||||||
|
|
||||||
|
func drop_tekton():
|
||||||
|
"""Drops the Tekton at the current player position immediately."""
|
||||||
|
if not is_multiplayer_authority() or not is_carrying_tekton:
|
||||||
|
return
|
||||||
|
|
||||||
|
if is_multiplayer_authority() and can_rpc():
|
||||||
|
rpc("sync_drop_tekton")
|
||||||
|
|
||||||
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
|
func sync_drop_tekton():
|
||||||
|
if carried_tekton:
|
||||||
|
var tekton = carried_tekton
|
||||||
|
carried_tekton = null
|
||||||
|
is_carrying_tekton = false
|
||||||
|
|
||||||
|
# Set its position to player's current position (but on ground)
|
||||||
|
var drop_pos = grid_to_world(current_position)
|
||||||
|
tekton.global_position = drop_pos
|
||||||
|
|
||||||
|
if tekton.has_method("set_carried"):
|
||||||
|
tekton.set_carried(false)
|
||||||
|
|
||||||
|
# Trigger landing effects (minimal scale)
|
||||||
|
if tekton.has_method("on_thrown_landing"):
|
||||||
|
tekton.on_thrown_landing(self, 1.0) # Minimal scale impact
|
||||||
|
|
||||||
|
print("[Player %s] Dropped Tekton at %s" % [name, current_position])
|
||||||
|
|
||||||
# is_attack_mode is already declared at top of file (or inherited?)
|
# is_attack_mode is already declared at top of file (or inherited?)
|
||||||
# Keeping is_knock_mode here for now or moving it up would be better, but let's just fix the error first.
|
# Keeping is_knock_mode here for now or moving it up would be better, but let's just fix the error first.
|
||||||
var is_knock_mode: bool = false # Yellow mode for knocking Tekton
|
var is_knock_mode: bool = false # Yellow mode for knocking Tekton
|
||||||
|
|||||||
@@ -120,26 +120,13 @@ func handle_unhandled_input(event):
|
|||||||
player.enter_attack_mode()
|
player.enter_attack_mode()
|
||||||
|
|
||||||
KEY_E:
|
KEY_E:
|
||||||
if player.powerup_manager:
|
if player.is_carrying_tekton and player.powerup_manager:
|
||||||
# Spawn Boost
|
# Spawn Boost (Now uses Tekton)
|
||||||
if player.powerup_manager.has_method("spawn_boost_reward"):
|
if player.powerup_manager.has_method("spawn_boost_reward"):
|
||||||
player.powerup_manager.spawn_boost_reward()
|
player.powerup_manager.spawn_boost_reward()
|
||||||
else:
|
|
||||||
# Fallback if method missing
|
|
||||||
player.powerup_manager.use_special_effect()
|
|
||||||
KEY_G:
|
KEY_G:
|
||||||
if player.is_carrying_tekton:
|
if not player.is_carrying_tekton:
|
||||||
if player.powerup_manager and player.powerup_manager.can_use_special():
|
|
||||||
player.throw_tekton()
|
|
||||||
player.powerup_manager.reset_boost()
|
|
||||||
else:
|
|
||||||
player.grab_tekton()
|
player.grab_tekton()
|
||||||
KEY_B:
|
|
||||||
if player.powerup_manager and player.powerup_manager.can_use_special():
|
|
||||||
if player.has_method("_find_nearby_tekton") and player._find_nearby_tekton():
|
|
||||||
player.knock_tekton()
|
|
||||||
elif player.has_method("enter_knock_mode"):
|
|
||||||
player.enter_knock_mode()
|
|
||||||
|
|
||||||
# Handle spawn point selection if not yet selected
|
# Handle spawn point selection if not yet selected
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,12 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
|||||||
if not other_player:
|
if not other_player:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# === INVULNERABILITY CHECK ===
|
||||||
|
if other_player.get("is_carrying_tekton"):
|
||||||
|
print("[Move] Push blocked: Target is carrying a Tekton and is invulnerable.")
|
||||||
|
NotificationManager.send_message(player, "Target is Immune!", NotificationManager.MessageType.WARNING)
|
||||||
|
return false
|
||||||
|
|
||||||
# === NEW LOGIC: Only allow push if in ATTACK MODE ===
|
# === NEW LOGIC: Only allow push if in ATTACK MODE ===
|
||||||
if not player.get("is_attack_mode"):
|
if not player.get("is_attack_mode"):
|
||||||
# Standard bumping effect or nothing?
|
# Standard bumping effect or nothing?
|
||||||
|
|||||||
@@ -479,9 +479,9 @@ func _refresh_tiles():
|
|||||||
|
|
||||||
for x in range(GRID_SIZE):
|
for x in range(GRID_SIZE):
|
||||||
for z in range(GRID_SIZE):
|
for z in range(GRID_SIZE):
|
||||||
# 1. Check if Floor 0 is a wall or empty (non-walkable)
|
# 1. Check if Floor 0 is a wall or void
|
||||||
var floor_0_item = gridmap.get_cell_item(Vector3i(x, 0, z))
|
var floor_0_item = gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||||
if floor_0_item == 4 or floor_0_item == -1:
|
if floor_0_item in [4, -1]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 1.5. Prevent spawning directly under portal doors
|
# 1.5. Prevent spawning directly under portal doors
|
||||||
|
|||||||
@@ -177,14 +177,20 @@ func consume_boost(amount: float):
|
|||||||
rpc("sync_boost", current_boost)
|
rpc("sync_boost", current_boost)
|
||||||
|
|
||||||
func spawn_boost_reward() -> bool:
|
func spawn_boost_reward() -> bool:
|
||||||
"""Alternative Boost Usage: Spawn a PowerUp Tile properly."""
|
"""Alternative Boost Usage: Spawn a PowerUp Tile properly.
|
||||||
if not can_use_special():
|
Now requires carrying a Tekton (E shortcut)."""
|
||||||
|
if not player.is_carrying_tekton:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if player.special_tiles_manager and player.special_tiles_manager.has_method("spawn_powerups_around"):
|
if player.special_tiles_manager and player.special_tiles_manager.has_method("spawn_powerups_around"):
|
||||||
player.special_tiles_manager.spawn_powerups_around(player.current_position)
|
# Spawn only common tiles (7-10) with 100% density
|
||||||
reset_boost() # Consumes full bar
|
player.special_tiles_manager.spawn_powerups_around(player.current_position, true, true, true)
|
||||||
print("[PowerUp] %s used Boost to SPAWN ITEM." % player.name)
|
|
||||||
|
# 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)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -377,20 +377,21 @@ func _execute_block_floor(target_pos: Vector2i = Vector2i.ZERO):
|
|||||||
if _is_position_blocked_by_stand(pos): continue
|
if _is_position_blocked_by_stand(pos): continue
|
||||||
|
|
||||||
var block_pos = Vector3i(pos.x, 0, pos.y)
|
var block_pos = Vector3i(pos.x, 0, pos.y)
|
||||||
var current_item = enhanced_gridmap.get_cell_item(block_pos)
|
var original_item = enhanced_gridmap.get_cell_item(block_pos)
|
||||||
|
|
||||||
|
# PROTECTED FLOOR CHECK: avoid overwriting Start (1), Safe (2), Finish (3), or Wall (4)
|
||||||
var is_immutable = false
|
var is_immutable = false
|
||||||
if "immutable_items" in enhanced_gridmap:
|
if "immutable_items" in enhanced_gridmap:
|
||||||
if current_item in enhanced_gridmap.immutable_items:
|
if original_item in enhanced_gridmap.immutable_items:
|
||||||
is_immutable = true
|
is_immutable = true
|
||||||
if current_item == 4 or is_immutable: continue
|
if original_item in [1, 2, 3, 4] or is_immutable: continue
|
||||||
|
|
||||||
batch_data.append({"x": block_pos.x, "y": 0, "z": block_pos.z, "item": 4})
|
batch_data.append({"x": block_pos.x, "y": 0, "z": block_pos.z, "item": 4})
|
||||||
|
|
||||||
# Record for restoration
|
# Record for restoration
|
||||||
blocked_tiles.append({
|
blocked_tiles.append({
|
||||||
"position": block_pos,
|
"position": block_pos,
|
||||||
"original_item": current_item,
|
"original_item": original_item,
|
||||||
"timer": BLOCK_DURATION
|
"timer": BLOCK_DURATION
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -415,7 +416,7 @@ func _execute_invisible_mode(target: Node3D):
|
|||||||
# Helper: Spawn Powerups (For Super Push)
|
# Helper: Spawn Powerups (For Super Push)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
func spawn_powerups_around(center: Vector2i, force_powerups: bool = true, only_common: bool = false, full_density: bool = false):
|
||||||
# "spawn / replace your nearby tiles into power up ( special tiles )"
|
# "spawn / replace your nearby tiles into power up ( special tiles )"
|
||||||
# New PowerUp Tiles are 11, 12, 13, 14
|
# New PowerUp Tiles are 11, 12, 13, 14
|
||||||
var radius = 2
|
var radius = 2
|
||||||
@@ -428,20 +429,31 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
|||||||
|
|
||||||
if enhanced_gridmap.is_position_valid(pos):
|
if enhanced_gridmap.is_position_valid(pos):
|
||||||
# Random chance to spawn ANYTHING at this spot (keep density reasonable)
|
# Random chance to spawn ANYTHING at this spot (keep density reasonable)
|
||||||
if rng.randf() > 0.5: continue
|
if not full_density and rng.randf() > 0.5: continue
|
||||||
|
|
||||||
|
# PROTECTED FLOOR CHECK: Don't spawn on existing walls or void
|
||||||
|
var f0 = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y))
|
||||||
|
var f1 = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 1, pos.y))
|
||||||
|
if f0 in [4, -1] or f1 == 4 or f1 == 13:
|
||||||
|
continue
|
||||||
|
|
||||||
var item_id: int
|
var item_id: int
|
||||||
# 70% Chance for Normal Tile (7-10)
|
var mode = LobbyManager.get_game_mode()
|
||||||
if rng.randf() < 0.7:
|
|
||||||
|
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
|
||||||
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)
|
# Free mode: 60% Chance for Normal Tile (7-10), 40% for PowerUp
|
||||||
var mode = LobbyManager.get_game_mode()
|
if rng.randf() < 0.6:
|
||||||
var is_restricted = GameMode.is_restricted(mode)
|
item_id = rng.randi_range(7, 10)
|
||||||
if is_restricted:
|
|
||||||
item_id = [11, 14].pick_random()
|
|
||||||
else:
|
else:
|
||||||
item_id = rng.randi_range(11, 14)
|
# 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes)
|
||||||
|
var is_restricted = GameMode.is_restricted(mode)
|
||||||
|
if is_restricted:
|
||||||
|
item_id = [11, 14].pick_random()
|
||||||
|
else:
|
||||||
|
item_id = rng.randi_range(11, 14)
|
||||||
|
|
||||||
var cell = Vector3i(pos.x, 1, pos.y)
|
var cell = Vector3i(pos.x, 1, pos.y)
|
||||||
|
|
||||||
|
|||||||
@@ -319,10 +319,17 @@ func _spawn_mission_tiles():
|
|||||||
for z in range(gridmap.rows):
|
for z in range(gridmap.rows):
|
||||||
# Ensure we don't spawn on obstacles
|
# Ensure we don't spawn on obstacles
|
||||||
var base_tile = gridmap.get_cell_item(Vector3i(x, 0, z))
|
var base_tile = gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||||
if base_tile == TILE_OBSTACLE:
|
var current_item = gridmap.get_cell_item(Vector3i(x, 1, z))
|
||||||
|
|
||||||
|
# PROTECTED FLOOR CHECK: Don't spawn on walls or void
|
||||||
|
if base_tile in [TILE_OBSTACLE, -1] or current_item == TILE_OBSTACLE or current_item == 13:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Spawn tiles with 60% density
|
||||||
|
if randf() > 0.6:
|
||||||
|
gridmap.set_cell_item(Vector3i(x, 1, z), -1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Spawn tiles on all floors (100% density)
|
|
||||||
var tile_type = goal_items[randi() % goal_items.size()]
|
var tile_type = goal_items[randi() % goal_items.size()]
|
||||||
gridmap.set_cell_item(Vector3i(x, 1, z), tile_type)
|
gridmap.set_cell_item(Vector3i(x, 1, z), tile_type)
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ var put_button: Button
|
|||||||
var attack_mode_button: Button # Renamed from special_button
|
var attack_mode_button: Button # Renamed from special_button
|
||||||
var spawn_boost_button: Button
|
var spawn_boost_button: Button
|
||||||
var settings_button: Button
|
var settings_button: Button
|
||||||
var tekton_knock_button: Button
|
var tekton_grab_button: Button
|
||||||
var tekton_throw_button: Button
|
|
||||||
|
|
||||||
# Settings - persisted to config file
|
# Settings - persisted to config file
|
||||||
const CONFIG_PATH = "user://touch_controls_settings.cfg"
|
const CONFIG_PATH = "user://touch_controls_settings.cfg"
|
||||||
@@ -137,15 +136,13 @@ func _create_touch_ui():
|
|||||||
grab_button = _find_or_create_action_button(actions_container, "Grab", "👋", button_positions.grab)
|
grab_button = _find_or_create_action_button(actions_container, "Grab", "👋", button_positions.grab)
|
||||||
put_button = _find_or_create_action_button(actions_container, "Put", "📦", button_positions.put)
|
put_button = _find_or_create_action_button(actions_container, "Put", "📦", button_positions.put)
|
||||||
|
|
||||||
tekton_knock_button = _find_or_create_action_button(actions_container, "TektonKnock", "👊", Vector2(-280, -160))
|
tekton_grab_button = _find_or_create_action_button(actions_container, "TektonGrab", "👋", Vector2(-280, -80))
|
||||||
tekton_throw_button = _find_or_create_action_button(actions_container, "TektonThrow", "🎯", Vector2(-280, -80))
|
|
||||||
|
|
||||||
# Order: AttackMode, SpawnBoost, Grab
|
# Order: AttackMode, SpawnBoost, Grab
|
||||||
if attack_mode_button: actions_container.move_child(attack_mode_button, 0)
|
if attack_mode_button: actions_container.move_child(attack_mode_button, 0)
|
||||||
if spawn_boost_button: actions_container.move_child(spawn_boost_button, 1)
|
if spawn_boost_button: actions_container.move_child(spawn_boost_button, 1)
|
||||||
if grab_button: actions_container.move_child(grab_button, 2)
|
if grab_button: actions_container.move_child(grab_button, 2)
|
||||||
if tekton_knock_button: actions_container.move_child(tekton_knock_button, 3)
|
if tekton_grab_button: actions_container.move_child(tekton_grab_button, 3)
|
||||||
if tekton_throw_button: actions_container.move_child(tekton_throw_button, 4)
|
|
||||||
|
|
||||||
# Hide Put Button
|
# Hide Put Button
|
||||||
if put_button:
|
if put_button:
|
||||||
@@ -263,8 +260,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String):
|
|||||||
"Put": existing_lbl.text = ""
|
"Put": existing_lbl.text = ""
|
||||||
"AttackMode": existing_lbl.text = "Q" if is_sng else ""
|
"AttackMode": existing_lbl.text = "Q" if is_sng else ""
|
||||||
"SpawnBoost": existing_lbl.text = "E" if is_sng else ""
|
"SpawnBoost": existing_lbl.text = "E" if is_sng else ""
|
||||||
"TektonKnock": existing_lbl.text = "B"
|
"TektonGrab": existing_lbl.text = "G"
|
||||||
"TektonThrow": existing_lbl.text = "G"
|
|
||||||
|
|
||||||
# Ensure correct placement (Top Right)
|
# Ensure correct placement (Top Right)
|
||||||
existing_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
existing_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
@@ -294,8 +290,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String):
|
|||||||
"Put": shortcut_lbl.text = "" # Disabled shortcut
|
"Put": shortcut_lbl.text = "" # Disabled shortcut
|
||||||
"AttackMode": shortcut_lbl.text = "Q"
|
"AttackMode": shortcut_lbl.text = "Q"
|
||||||
"SpawnBoost": shortcut_lbl.text = "E"
|
"SpawnBoost": shortcut_lbl.text = "E"
|
||||||
"TektonKnock": shortcut_lbl.text = "B"
|
"TektonGrab": shortcut_lbl.text = "G"
|
||||||
"TektonThrow": shortcut_lbl.text = "G"
|
|
||||||
|
|
||||||
btn.add_child(shortcut_lbl)
|
btn.add_child(shortcut_lbl)
|
||||||
|
|
||||||
@@ -315,8 +310,7 @@ func _on_button_pressed(button_name: String):
|
|||||||
"Put": btn = put_button
|
"Put": btn = put_button
|
||||||
"AttackMode": btn = attack_mode_button
|
"AttackMode": btn = attack_mode_button
|
||||||
"SpawnBoost": btn = spawn_boost_button
|
"SpawnBoost": btn = spawn_boost_button
|
||||||
"TektonKnock": btn = tekton_knock_button
|
"TektonGrab": btn = tekton_grab_button
|
||||||
"TektonThrow": btn = tekton_throw_button
|
|
||||||
|
|
||||||
if btn:
|
if btn:
|
||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
@@ -347,27 +341,11 @@ func _on_button_pressed(button_name: String):
|
|||||||
else:
|
else:
|
||||||
print("[TouchControls] PowerUpManager missing on player")
|
print("[TouchControls] PowerUpManager missing on player")
|
||||||
"SpawnBoost":
|
"SpawnBoost":
|
||||||
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
if local_player and local_player.is_carrying_tekton:
|
||||||
if powerup_mgr and powerup_mgr.can_use_special():
|
if local_player.powerup_manager and local_player.powerup_manager.has_method("spawn_boost_reward"):
|
||||||
if local_player.special_tiles_manager and local_player.special_tiles_manager.has_method("spawn_powerups_around"):
|
local_player.powerup_manager.spawn_boost_reward()
|
||||||
local_player.special_tiles_manager.spawn_powerups_around(local_player.current_position)
|
"TektonGrab":
|
||||||
powerup_mgr.reset_boost()
|
if not local_player.is_carrying_tekton:
|
||||||
"TektonKnock":
|
|
||||||
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
|
||||||
if powerup_mgr and powerup_mgr.can_use_special():
|
|
||||||
# Proactive: If nearby, knock immediately. Else enter mode.
|
|
||||||
if local_player.has_method("_find_nearby_tekton") and local_player._find_nearby_tekton():
|
|
||||||
local_player.knock_tekton()
|
|
||||||
elif local_player.has_method("enter_knock_mode"):
|
|
||||||
local_player.enter_knock_mode()
|
|
||||||
"TektonThrow":
|
|
||||||
if local_player.is_carrying_tekton:
|
|
||||||
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
|
||||||
if powerup_mgr and powerup_mgr.can_use_special():
|
|
||||||
if local_player.has_method("throw_tekton"):
|
|
||||||
local_player.throw_tekton()
|
|
||||||
powerup_mgr.reset_boost()
|
|
||||||
else:
|
|
||||||
if local_player.has_method("grab_tekton"):
|
if local_player.has_method("grab_tekton"):
|
||||||
local_player.grab_tekton()
|
local_player.grab_tekton()
|
||||||
|
|
||||||
@@ -378,8 +356,7 @@ func _on_button_released(button_name: String):
|
|||||||
"Put": btn = put_button
|
"Put": btn = put_button
|
||||||
"AttackMode": btn = attack_mode_button
|
"AttackMode": btn = attack_mode_button
|
||||||
"SpawnBoost": btn = spawn_boost_button
|
"SpawnBoost": btn = spawn_boost_button
|
||||||
"TektonKnock": btn = tekton_knock_button
|
"TektonGrab": btn = tekton_grab_button
|
||||||
"TektonThrow": btn = tekton_throw_button
|
|
||||||
|
|
||||||
if btn:
|
if btn:
|
||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
@@ -548,12 +525,12 @@ func _on_boost_points_changed(current_points: int, max_points: int):
|
|||||||
var is_full = current_points >= (max_points - 1) # Tolerance
|
var is_full = current_points >= (max_points - 1) # Tolerance
|
||||||
|
|
||||||
_update_boost_button_state(attack_mode_button, is_full)
|
_update_boost_button_state(attack_mode_button, is_full)
|
||||||
_update_boost_button_state(spawn_boost_button, is_full)
|
|
||||||
_update_boost_button_state(tekton_knock_button, is_full)
|
|
||||||
|
|
||||||
# TektonThrow can be used for "Grab" even without full boost
|
# SpawnBoost depends on carrying a Tekton, not boost points
|
||||||
var can_throw_or_grab = is_full or (local_player and not local_player.is_carrying_tekton)
|
var can_spawn = local_player and local_player.is_carrying_tekton
|
||||||
_update_boost_button_state(tekton_throw_button, can_throw_or_grab)
|
_update_boost_button_state(spawn_boost_button, can_spawn)
|
||||||
|
|
||||||
|
_update_boost_button_state(tekton_grab_button, true) # Always enabled (logic handles grab-ability)
|
||||||
|
|
||||||
func _on_tekton_carried_changed(_is_carrying: bool):
|
func _on_tekton_carried_changed(_is_carrying: bool):
|
||||||
# Refresh button states when player grabs/throws a tekton
|
# Refresh button states when player grabs/throws a tekton
|
||||||
|
|||||||
+4
-3
@@ -299,7 +299,8 @@ func temporarily_change_floor(center: Vector2i, radius: int, new_id: int, durati
|
|||||||
var original = enhanced_gridmap.get_cell_item(cell_3d)
|
var original = enhanced_gridmap.get_cell_item(cell_3d)
|
||||||
|
|
||||||
# Only change if not already the new ID (avoid redundant updates or overriding existing freeze)
|
# Only change if not already the new ID (avoid redundant updates or overriding existing freeze)
|
||||||
if original != new_id:
|
# PROTECTED FLOOR CHECK: avoid overwriting Start (1), Safe (2), Finish (3), or Wall (4)
|
||||||
|
if original != new_id and not original in [1, 2, 3, 4]:
|
||||||
# PRE-FIX: If we capture a "Hole" (Void or Pickup 7-14) here, we must record it as Floor (0)
|
# PRE-FIX: If we capture a "Hole" (Void or Pickup 7-14) here, we must record it as Floor (0)
|
||||||
# so that we restore a valid floor later.
|
# so that we restore a valid floor later.
|
||||||
if original == -1 or (original >= 7 and original <= 14):
|
if original == -1 or (original >= 7 and original <= 14):
|
||||||
@@ -370,10 +371,10 @@ func spawn_tiles_around(count: int = 4):
|
|||||||
if _is_position_blocked_by_stand(pos):
|
if _is_position_blocked_by_stand(pos):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if enhanced_gridmap.is_position_valid(pos):
|
|
||||||
# EXTRA CHECK: Do not spawn tiles on walls (ID 4) or empty void (ID -1) on Floor 0
|
# EXTRA CHECK: Do not spawn tiles on walls (ID 4) or empty void (ID -1) on Floor 0
|
||||||
|
# Note: We allow spawning on Safe Zones, Start, and Finish as it's on Layer 1.
|
||||||
var floor_0_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y))
|
var floor_0_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y))
|
||||||
if floor_0_item == 4 or floor_0_item == -1:
|
if floor_0_item in [4, -1]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 50% chance to spawn something
|
# 50% chance to spawn something
|
||||||
|
|||||||
Reference in New Issue
Block a user