feat: Implement powerup inventory UI and manager, introducing ghost and area freeze special tiles.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
[ext_resource type="ArrayMesh" uid="uid://brevl3ab0tdqe" path="res://assets/models/tiles/tile_wall.tres" id="4_8v5xv"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://b5ta7tcw0iscd" path="res://assets/models/tiles/tile_coin.tres" id="4_76xkl"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://d4himvyb81in8" path="res://assets/models/meshes/non-walkable.res" id="4_sx8rm"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://dr80txgr61irt" path="res://assets/models/tiles/tile_area_freeze.tres" id="5_sx8rm"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://dqo83gvaay6sn" path="res://assets/models/tiles/tile_area_freeze.tres" id="5_sx8rm"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://bqvqj3fhf5x51" path="res://assets/models/tiles/tile_ghost.tres" id="6_r32il"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://cv4bedhida00g" path="res://assets/models/tiles/tile_star.tres" id="7_p5epg"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://gpnl4cjrivor" path="res://assets/models/tiles/tile_speed.tres" id="7_sx8rm"]
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b2vhatfmufn3d"
|
||||
path="res://.godot/imported/ghost.png-50b35ecd19732e2631cec7740daf0cec.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/touch_control/ghost.png"
|
||||
dest_files=["res://.godot/imported/ghost.png-50b35ecd19732e2631cec7740daf0cec.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,41 +1,41 @@
|
||||
[gd_resource type="ArrayMesh" format=4 uid="uid://dr80txgr61irt"]
|
||||
[gd_resource type="ArrayMesh" format=4 uid="uid://dqo83gvaay6sn"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cp7jtg77hc078" path="res://assets/textures/power_tile/freeze_area_tile.png" id="1_3pijd"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2kxvc"]
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ev6sk"]
|
||||
resource_name = "boost"
|
||||
cull_mode = 2
|
||||
albedo_color = Color(0.905882, 0.905882, 0.905882, 0.45098)
|
||||
albedo_color = Color(0.91, 0.91, 0.91, 0.45098)
|
||||
albedo_texture = ExtResource("1_3pijd")
|
||||
|
||||
[sub_resource type="ArrayMesh" id="ArrayMesh_ggb2g"]
|
||||
[sub_resource type="ArrayMesh" id="ArrayMesh_w3aij"]
|
||||
_surfaces = [{
|
||||
"aabb": AABB(-0.282176, -0.000324821, -0.282176, 0.564351, 0.050792, 0.564351),
|
||||
"format": 34896613377,
|
||||
"index_count": 36,
|
||||
"index_data": PackedByteArray("AAABAAIAAAADAAEABAABAAMABQADAAAABQAEAAMAAgAFAAAABAAGAAEAAgABAAYABgAEAAUAAgAHAAUABgAFAAcAAgAGAAcA"),
|
||||
"index_data": PackedByteArray("BwAEAAUABwAGAAQABQACAAMABQAEAAIAAAAEAAYAAAACAAQABQABAAcABQADAAEAAgABAAMAAgAAAAEAAQAGAAcAAQAAAAYA"),
|
||||
"name": "boost",
|
||||
"primitive": 3,
|
||||
"uv_scale": Vector4(0, 0, 0, 0),
|
||||
"vertex_count": 8,
|
||||
"vertex_data": PackedByteArray("AAAAAAAAAAD//wAA//8AAP//AAAAAAAAAAAAAP//AAAAAP////8AAAAA/v8AAAAA////////AAD///7/AAAAAA==")
|
||||
"vertex_data": PackedByteArray("AAD/////AAAAAP7/AAAAAP///////wAA///+/wAAAAD//wAA//8AAP//AAAAAAAAAAAAAP//AAAAAAAAAAAAAA==")
|
||||
}]
|
||||
blend_shape_mode = 0
|
||||
|
||||
[resource]
|
||||
resource_name = "tile_tile_coin_002"
|
||||
resource_name = "tile_star"
|
||||
_surfaces = [{
|
||||
"aabb": AABB(-0.282176, -0.000324821, -0.282176, 0.564351, 0.050792, 0.564351),
|
||||
"attribute_data": PackedByteArray("zuS25MX6sfrF+rbkzuSx+szkQOSr9qfkzOSn5Kv2QOSw+yzlSvvl70r7LOWw++XvSvu0+rD7+u9K+/rvsPu0+pHeot5tIVwhkd5cIW0hot47+6/61PrQ6NT6r/o7+9Do"),
|
||||
"attribute_data": PackedByteArray("sPss5W0hot47+9DosPv6720hXCE7+6/6sPvl75Heot6r9qfksPu0+pHeXCHM5KfkSvvl78X6sfqr9kDkSvu0+sX6tuTM5EDkSvss5c7ksfrU+tDoSvv6787ktuTU+q/6"),
|
||||
"format": 34896613399,
|
||||
"index_count": 36,
|
||||
"index_data": PackedByteArray("AAABAAIAAAADAAEABAAFAAYABAAHAAUACAAJAAoACAALAAkADAANAA4ADAAPAA0AEAARABIAEAATABEAFAAVABYAFAAXABUA"),
|
||||
"material": SubResource("StandardMaterial3D_2kxvc"),
|
||||
"name": "tile_diamond_holo",
|
||||
"index_data": PackedByteArray("FgANABAAFgATAA0AEQAIAAsAEQAOAAgAAAAMABIAAAAGAAwADwADABUADwAJAAMABwAEAAoABwABAAQABQAUABcABQACABQA"),
|
||||
"material": SubResource("StandardMaterial3D_ev6sk"),
|
||||
"name": "tile_area_freeze",
|
||||
"primitive": 3,
|
||||
"uv_scale": Vector4(0, 0, 0, 0),
|
||||
"vertex_count": 24,
|
||||
"vertex_data": PackedByteArray("AAAAAAAA/z///wAA////P///AAAAAP8/AAAAAP///z///wAAAAD////////////////+/wAA/////wAA/////wAA//////+///8AAP///78AAAAA////v/////////+///8AAAAAAAAAAP7/AAAAAAAAAAAAAAAA///+/wAAAAD/////////vwAA/v8AAP+////+/wAA/78AAP//////vwAA/v8AAKoqAAAAAP//qioAAAAAAACqKgAA/////6oqAAD/fwAA/38AAP9/AAD/f/+//3//v/9//7//f/+//3///////////////////////7//v/+//7//v/+//7//v////3////9/////f////39U1VTVVNVU1VTVVNVU1VTV")
|
||||
"vertex_data": PackedByteArray("AAD//////78AAP//////vwAA/////6oqAAD+/wAAAAAAAP7/AAD/vwAA/v8AAKoq/////////7//////////v//////////////+/wAAAAD///7/AAD/v////v8AAP////8AAP///7///wAA////P///AAD///////8AAAAAAAD//wAAAAD/P///AAAAAP//AAAAAP///78AAAAA////PwAAAAD//6oqAAAAAAAAAAAAAAAAAAD/PwAAAAAAAKoq/////////39U1VTV/7//v////39U1VTV/////////3//v/9//7//v////3//v/9//////wAA/3//v/9//7//vwAA/3//v/9//////wAA/39U1VTV/7//vwAA/39U1VTV")
|
||||
}]
|
||||
blend_shape_mode = 0
|
||||
shadow_mesh = SubResource("ArrayMesh_ggb2g")
|
||||
shadow_mesh = SubResource("ArrayMesh_w3aij")
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
[gd_resource type="ArrayMesh" format=4 uid="uid://bqvqj3fhf5x51"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dv0datyodktqu" path="res://assets/textures/power_tile/ghost_tile.png" id="1_avhe4"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ev6sk"]
|
||||
resource_name = "boost"
|
||||
cull_mode = 2
|
||||
albedo_color = Color(0.91, 0.91, 0.91, 0.45098)
|
||||
albedo_texture = ExtResource("1_avhe4")
|
||||
|
||||
[sub_resource type="ArrayMesh" id="ArrayMesh_w3aij"]
|
||||
_surfaces = [{
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dv0datyodktqu"
|
||||
path.s3tc="res://.godot/imported/ghost_tile.png-472e4da46a96c33de653b9d0ab2de179.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/power_tile/ghost_tile.png"
|
||||
dest_files=["res://.godot/imported/ghost_tile.png-472e4da46a96c33de653b9d0ab2de179.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
+2
-1
@@ -29,6 +29,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://cupfmb5m15kmf" path="res://assets/graphics/touch_control/wall.png" id="27_yq6so"]
|
||||
[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://b2vhatfmufn3d" path="res://assets/graphics/touch_control/ghost.png" id="33_5q0nq"]
|
||||
[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://djiml4sh61dc1" path="res://scripts/ui/virtual_joystick.gd" id="virtual_joystick"]
|
||||
@@ -9881,7 +9882,7 @@ expand_icon = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
icon = ExtResource("27_dgi5k")
|
||||
icon = ExtResource("33_5q0nq")
|
||||
flat = true
|
||||
icon_alignment = 1
|
||||
expand_icon = true
|
||||
|
||||
+42
-2
@@ -613,11 +613,20 @@ func _apply_tint_recursive(node: Node, color: Color):
|
||||
# If color is WHITE (reset), clear the overlay
|
||||
if color == Color.WHITE:
|
||||
node.material_overlay = null
|
||||
node.transparency = 0.0 # Reset
|
||||
else:
|
||||
# If color is Blue (frozen), make it semi-transparent overlay
|
||||
mat.albedo_color = color
|
||||
mat.albedo_color.a = 0.5 # Semi-transparent
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
|
||||
# Special Ghost Effect (Invisible Mode)
|
||||
if color.a < 1.0:
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
node.transparency = 0.5 # Make base mesh transparent too if possible, depending on material
|
||||
else:
|
||||
# Frozen
|
||||
mat.albedo_color.a = 0.5 # Semi-transparent overlay
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
|
||||
node.material_overlay = mat
|
||||
|
||||
for child in node.get_children():
|
||||
@@ -658,6 +667,37 @@ func apply_stagger(duration: float = 1.5):
|
||||
else:
|
||||
_apply_tint_recursive(self, Color.WHITE) # Remove tint
|
||||
|
||||
@rpc("any_peer", "call_local")
|
||||
func apply_slow_effect(duration: float = 3.0):
|
||||
# "area with blue like wall... Player who cross on that area will got slowed and freeze effect"
|
||||
# Visual: Blue Tint
|
||||
_apply_tint_recursive(self, Color(0.6, 0.8, 1.0)) # Icy Blue
|
||||
|
||||
# Logic: Slow Movement speed
|
||||
if movement_manager:
|
||||
# Use 0.2 multipliers to match "slowed" request (20% speed)
|
||||
movement_manager.set_speed_multiplier(0.2)
|
||||
|
||||
print("Player %s is slowed for %.1f seconds" % [name, duration])
|
||||
|
||||
# Restore after duration
|
||||
# Note: If they stand in the zone, this will be re-applied constantly, resetting the visual
|
||||
await get_tree().create_timer(duration).timeout
|
||||
|
||||
# Only restore if not re-applied recently (simple check: if still tinted?)
|
||||
# A better way is managing a "slow_timer" but for now let's just reset if timer expires.
|
||||
# The persistent zone logic reapplies every frame, so we want this timer to be short
|
||||
# OR we rely on the zone logic.
|
||||
# The RPC call says "apply_slow_effect(0.5)", so it expires quickly.
|
||||
|
||||
if movement_manager:
|
||||
movement_manager.set_speed_multiplier(1.0)
|
||||
|
||||
if immunity_timer > 0:
|
||||
_apply_tint_recursive(self, Color(0.5, 1.0, 0.5))
|
||||
else:
|
||||
_apply_tint_recursive(self, Color.WHITE)
|
||||
|
||||
func playerboard_is_empty() -> bool:
|
||||
for item in playerboard:
|
||||
if item != -1:
|
||||
|
||||
@@ -70,7 +70,11 @@ func simple_move_to(grid_position: Vector2i) -> bool:
|
||||
return false
|
||||
|
||||
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y))
|
||||
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items:
|
||||
|
||||
# Allow passing through Walls (Item 4) if Invisible
|
||||
var is_wall_passable = player.get("is_invisible") and cell_item == 4
|
||||
|
||||
if (cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items) and not is_wall_passable:
|
||||
return false
|
||||
|
||||
if player.is_position_occupied(grid_position):
|
||||
@@ -234,7 +238,14 @@ func highlight_movement_range():
|
||||
|
||||
# Check basic walkability
|
||||
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items or player.is_position_occupied(test_pos):
|
||||
|
||||
# Allow passing through Walls (Item 4) if Invisible
|
||||
var is_wall_passable = player.get("is_invisible") and cell_item == 4
|
||||
|
||||
if (cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items) and not is_wall_passable:
|
||||
continue
|
||||
|
||||
if player.is_position_occupied(test_pos):
|
||||
continue
|
||||
|
||||
# Check if there's a valid path to this cell
|
||||
@@ -284,8 +295,14 @@ func can_reach_cell(target_pos: Vector2i, blocked_cells: Array) -> bool:
|
||||
# Skip if already visited, blocked, or not valid
|
||||
if visited.has(next_pos) or next_pos in blocked_cells:
|
||||
continue
|
||||
|
||||
if not enhanced_gridmap.is_position_valid(next_pos) or not enhanced_gridmap.is_cell_walkable(next_pos, 0):
|
||||
|
||||
# Custom Walkable Check incorporating invisibility
|
||||
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(next_pos.x, 0, next_pos.y))
|
||||
var is_wall_passable = player.get("is_invisible") and cell_item == 4
|
||||
|
||||
if (not enhanced_gridmap.is_position_valid(next_pos) or \
|
||||
(cell_item in enhanced_gridmap.non_walkable_items and not is_wall_passable) or \
|
||||
cell_item == -1):
|
||||
continue
|
||||
|
||||
if player.is_position_occupied(next_pos) and next_pos != target_pos:
|
||||
|
||||
@@ -52,8 +52,10 @@ const INVISIBLE_DURATION = 6.0
|
||||
# Active effect tracking
|
||||
var blocked_tiles: Array[Dictionary] = [] # {position: Vector3i, original_item: int, timer: float}
|
||||
var freeze_zones: Array[Dictionary] = [] # {position: Vector2i, timer: float}
|
||||
var active_freeze_zones: Array = [] # Array of {center, radius, timer}
|
||||
var invisible_timer: float = 0.0
|
||||
|
||||
|
||||
# INVENTORY SYSTEM
|
||||
# Stores count of each power-up type. Max 1 per type as per user request?
|
||||
# "player can store 1 of each different power up"
|
||||
@@ -189,47 +191,107 @@ func _execute_faster_speed():
|
||||
NotificationManager.send_message(player, "Speed Boost! (5s)", NotificationManager.MessageType.POWERUP)
|
||||
|
||||
func _execute_area_freeze():
|
||||
# "Area Freeze... slow their speed movement for 3s"
|
||||
# Reuse freeze logic but simpler duration
|
||||
var center = player.current_position
|
||||
# 3x3 around player
|
||||
var radius = 1
|
||||
print("Player %s executing Area Freeze" % player.name)
|
||||
# "area with blue like wall but with far away from the player who use it"
|
||||
# "Make it like 4 floor first (offset 4) and the continue to bigger when the level... is close to max"
|
||||
|
||||
# Get enemies in radius
|
||||
# 1. Calculate Forward Direction based on Rotation
|
||||
# Rotation 0 = South (+Z), PI = North (-Z)
|
||||
var rot = player.rotation.y
|
||||
var forward_x = round(sin(rot))
|
||||
var forward_z = round(cos(rot))
|
||||
var forward_vec = Vector2i(forward_x, forward_z)
|
||||
|
||||
# If rotation is diagonal or imprecise, normalize to cardinal
|
||||
if abs(forward_x) > abs(forward_z):
|
||||
forward_vec = Vector2i(sign(forward_x), 0)
|
||||
else:
|
||||
forward_vec = Vector2i(0, sign(forward_z))
|
||||
|
||||
# 2. Offset Center (4 tiles away)
|
||||
var offset_dist = 4
|
||||
var center = player.current_position + (forward_vec * offset_dist)
|
||||
|
||||
# 3. Determine Radius based on Level
|
||||
# Level 1-4: Radius 1 (3x3 area)
|
||||
# Level 5-8: Radius 2 (5x5 area)
|
||||
var current_lvl = powerup_levels.get(SpecialEffect.AREA_FREEZE, 1)
|
||||
var radius = 1
|
||||
if current_lvl >= 5:
|
||||
radius = 2 # Bigger area at high levels
|
||||
|
||||
print("Player %s executing Area Freeze at %s (Offset %s, Lvl %d, Rad %d)" % [player.name, center, forward_vec, current_lvl, radius])
|
||||
|
||||
# Register Zone for persistence
|
||||
active_freeze_zones.append({
|
||||
"center": center,
|
||||
"radius": radius,
|
||||
"timer": FREEZE_SLOW_DURATION # Same duration as the visual
|
||||
})
|
||||
|
||||
# Initial Check (Instant Feedback)
|
||||
var all_players = player.get_tree().get_nodes_in_group("Players")
|
||||
for p in all_players:
|
||||
if p == player: continue
|
||||
var dist = Vector2(p.current_position.x - center.x, p.current_position.y - center.y).length()
|
||||
if dist <= 1.5: # Adjacent or on top
|
||||
# Check distance (Chebyshev distance for square area)
|
||||
var dx = abs(p.current_position.x - center.x)
|
||||
var dy = abs(p.current_position.y - center.y)
|
||||
|
||||
# If inside square radius
|
||||
if dx <= radius and dy <= radius:
|
||||
p.rpc("apply_slow_effect", FREEZE_SLOW_DURATION)
|
||||
NotificationManager.send_message(p, "Caught in Freeze Zone!", NotificationManager.MessageType.WARNING)
|
||||
|
||||
# Visual Feedback (Icy Floor for 3s?)
|
||||
# Visual Feedback (Turn Floor Blue - Item 12 on Layer 0)
|
||||
if player.is_multiplayer_authority():
|
||||
for x in range(-1, 2):
|
||||
for y in range(-1, 2):
|
||||
# Sync Icy Floor (Layer 0)
|
||||
for x in range(-radius, radius + 1):
|
||||
for y in range(-radius, radius + 1):
|
||||
var pos = center + Vector2i(x, y)
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
if main: main.rpc("sync_grid_item", pos.x, 2, pos.y, 15) # Icy decal
|
||||
if enhanced_gridmap.is_position_valid(pos):
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
# Use Item 12 (Blue Freeze Tile) on Layer 0 (Floor)
|
||||
if main: main.rpc("sync_grid_item", pos.x, 0, pos.y, 12)
|
||||
|
||||
# Cleanup visual timer (managed locally by author)
|
||||
get_tree().create_timer(FREEZE_SLOW_DURATION).timeout.connect(func():
|
||||
for x in range(-1, 2):
|
||||
for y in range(-1, 2):
|
||||
for x in range(-radius, radius + 1):
|
||||
for y in range(-radius, radius + 1):
|
||||
var pos = center + Vector2i(x, y)
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
if main: main.rpc("sync_grid_item", pos.x, 2, pos.y, -1)
|
||||
if enhanced_gridmap.is_position_valid(pos):
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
# Restore to Item 0 (Standard Floor)
|
||||
if main: main.rpc("sync_grid_item", pos.x, 0, pos.y, 0)
|
||||
)
|
||||
|
||||
func _execute_block_floor(target: Node3D):
|
||||
# Existing logic for blocking, reused
|
||||
# "Wall Block" usually means block WHERE YOU ARE or FRONT?
|
||||
# Original code blocked target's floor.
|
||||
# If target is self, blocks self's floor? Maybe defensive.
|
||||
# "Have the same cooldown as Faster" -> Just logic reuse.
|
||||
# User Request: "choose one between horizontal or vertical all the way to the colus or rows"
|
||||
# We interpret "Choose one" as random 50/50 since there's no UI for sub-selection.
|
||||
|
||||
# Check for Immunity (Invisible Mode)
|
||||
if target.get("is_invisible"):
|
||||
NotificationManager.send_message(target, "blocked!", NotificationManager.MessageType.POWERUP)
|
||||
# We should probably notify the attacker too?
|
||||
return
|
||||
|
||||
var center = target.current_position
|
||||
var neighbors = enhanced_gridmap.get_neighbors(center, 1)
|
||||
neighbors.append({"position": center})
|
||||
var is_horizontal = rng.randf() < 0.5
|
||||
var neighbors = []
|
||||
|
||||
if is_horizontal:
|
||||
# Block entire Row (Fixed Z, iterate all X)
|
||||
# Assuming 'center.y' corresponds to Grid Z-row
|
||||
var row_z = center.y
|
||||
for x in range(enhanced_gridmap.columns):
|
||||
neighbors.append({"position": Vector2i(x, row_z)})
|
||||
print("Player %s activated Wall Block: HORIZONTAL ROW (Z=%d)" % [player.name, row_z])
|
||||
else:
|
||||
# Block entire Column (Fixed X, iterate all Z)
|
||||
var col_x = center.x
|
||||
for z in range(enhanced_gridmap.rows):
|
||||
neighbors.append({"position": Vector2i(col_x, z)})
|
||||
print("Player %s activated Wall Block: VERTICAL COLUMN (X=%d)" % [player.name, col_x])
|
||||
|
||||
for n in neighbors:
|
||||
var pos = n.position
|
||||
@@ -240,8 +302,9 @@ func _execute_block_floor(target: Node3D):
|
||||
if main:
|
||||
main.rpc("sync_grid_item", block_pos.x, block_pos.y, block_pos.z, 4)
|
||||
|
||||
var original_item = 0 # Assume floor
|
||||
# If we have logic to save original, fine, but for now just 0
|
||||
# We don't save original item here properly in this loop if we overwrite something important,
|
||||
# but for Floor 0 it's usually just ground (0) or obstacles.
|
||||
# If we overwrite another Wall, it's fine.
|
||||
blocked_tiles.append({
|
||||
"position": block_pos,
|
||||
"original_item": 0,
|
||||
@@ -250,9 +313,13 @@ func _execute_block_floor(target: Node3D):
|
||||
NotificationManager.send_message(target, "Wall Block Created!", NotificationManager.MessageType.POWERUP)
|
||||
|
||||
func _execute_invisible_mode(target: Node3D):
|
||||
# Existing logic kept as ID 14 placeholder
|
||||
target.is_invisible = true
|
||||
invisible_timer = INVISIBLE_DURATION
|
||||
|
||||
# Visual Feedback: Ghost Mode (Low Alpha)
|
||||
if target.has_method("sync_modulate"):
|
||||
target.rpc("sync_modulate", Color(1.0, 1.0, 1.0, 0.4)) # 40% Opacity
|
||||
|
||||
NotificationManager.send_message(target, "Invisible Mode!", NotificationManager.MessageType.POWERUP)
|
||||
|
||||
|
||||
@@ -273,12 +340,12 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
||||
if rng.randf() > 0.5: continue
|
||||
|
||||
var item_id: int
|
||||
# 70% Chance for PowerUp (11-14)
|
||||
# 70% Chance for Normal Tile (7-10)
|
||||
if rng.randf() < 0.7:
|
||||
item_id = rng.randi_range(11, 14)
|
||||
else:
|
||||
# 30% Chance for Normal Tile (7-10)
|
||||
item_id = rng.randi_range(7, 10)
|
||||
else:
|
||||
# 30% Chance for PowerUp (11-14)
|
||||
item_id = rng.randi_range(11, 14)
|
||||
|
||||
var cell = Vector3i(pos.x, 1, pos.y)
|
||||
|
||||
@@ -290,23 +357,35 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
||||
|
||||
func _update_freeze_zones(delta: float):
|
||||
# Only the authority of this manager (the caster) handles the timers and cleanup
|
||||
if not player.is_multiplayer_authority():
|
||||
return
|
||||
if not active_freeze_zones.is_empty():
|
||||
var zones_to_remove = []
|
||||
|
||||
var zones_to_remove = []
|
||||
for i in range(freeze_zones.size()):
|
||||
freeze_zones[i].timer -= delta
|
||||
if freeze_zones[i].timer <= 0:
|
||||
zones_to_remove.append(i)
|
||||
for i in range(active_freeze_zones.size()):
|
||||
var zone = active_freeze_zones[i]
|
||||
zone.timer -= delta
|
||||
|
||||
# Cleanup expired zones
|
||||
zones_to_remove.reverse()
|
||||
for idx in zones_to_remove:
|
||||
var zone = freeze_zones[idx]
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
if main:
|
||||
main.rpc("sync_grid_item", zone.position.x, 2, zone.position.y, -1)
|
||||
freeze_zones.remove_at(idx)
|
||||
# Check for players inside this zone (Trap Logic)
|
||||
var all_players = player.get_tree().get_nodes_in_group("Players")
|
||||
for p in all_players:
|
||||
# Invisible Immunity (Passive)
|
||||
if p.get("is_invisible"): continue
|
||||
|
||||
var dx = abs(p.current_position.x - zone.center.x)
|
||||
var dy = abs(p.current_position.y - zone.center.y)
|
||||
|
||||
# If inside zone
|
||||
if dx <= zone.radius and dy <= zone.radius:
|
||||
# Apply slow effect repeatedly
|
||||
# We use a short duration so it expires quickly if they leave
|
||||
p.rpc("apply_slow_effect", 0.5)
|
||||
|
||||
if zone.timer <= 0:
|
||||
zones_to_remove.append(i)
|
||||
|
||||
# Cleanup expired zones
|
||||
zones_to_remove.reverse()
|
||||
for idx in zones_to_remove:
|
||||
active_freeze_zones.remove_at(idx)
|
||||
|
||||
func _check_for_icy_floor():
|
||||
# Every player checks if they are standing on an icy floor (item 15 on layer 2)
|
||||
@@ -314,6 +393,10 @@ func _check_for_icy_floor():
|
||||
if not player.is_multiplayer_authority():
|
||||
return
|
||||
|
||||
# Invisible Immunity (Passive)
|
||||
if player.is_invisible:
|
||||
return
|
||||
|
||||
if not enhanced_gridmap:
|
||||
return
|
||||
|
||||
@@ -339,7 +422,6 @@ func _process(delta):
|
||||
if powerup_cooldowns[effect] <= 0:
|
||||
powerup_cooldowns[effect] = 0
|
||||
emit_signal("cooldown_updated", effect, 0, 0)
|
||||
print("Cooldown finished for %s" % SpecialEffect.keys()[effect])
|
||||
|
||||
# Update Active Buffs (Speed)
|
||||
if active_buffs.has(SpecialEffect.FASTER_SPEED):
|
||||
@@ -392,7 +474,11 @@ func _update_invisible_timer(delta: float):
|
||||
invisible_timer = 0
|
||||
if is_instance_valid(player):
|
||||
player.is_invisible = false
|
||||
NotificationManager.send_message(player, NotificationManager.MESSAGES.INVISIBILITY_ENDED, NotificationManager.MessageType.NORMAL)
|
||||
# Reset Visuals
|
||||
if player.has_method("sync_modulate"):
|
||||
player.rpc("sync_modulate", Color.WHITE)
|
||||
|
||||
NotificationManager.send_message(player, "Invisibility Ended", NotificationManager.MessageType.NORMAL)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -160,7 +160,7 @@ func _on_cooldown_updated(effect: int, time_left: float, max_time: float):
|
||||
var cd_lbl = btn.get_node_or_null("CooldownLabel")
|
||||
if cd_lbl:
|
||||
if time_left > 0:
|
||||
cd_lbl.text = "%.1f" % time_left
|
||||
cd_lbl.text = "%d" % int(time_left)
|
||||
btn.disabled = true
|
||||
btn.modulate = Color(0.7, 0.7, 0.7, 0.8)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user