feat: Implement the core "Tekton Doors" game mode including arena setup, portal management, special tiles, and mission tracking HUD.
Tekton Armageddon V.0.8
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
[ext_resource type="ArrayMesh" uid="uid://onkud44h3we1" path="res://assets/models/meshes/safe_zone.res" id="8_cg50n"]
|
[ext_resource type="ArrayMesh" uid="uid://onkud44h3we1" path="res://assets/models/meshes/safe_zone.res" id="8_cg50n"]
|
||||||
[ext_resource type="ArrayMesh" uid="uid://dx41n2x8v30r1" path="res://assets/models/meshes/crack.res" id="10_r32il"]
|
[ext_resource type="ArrayMesh" uid="uid://dx41n2x8v30r1" path="res://assets/models/meshes/crack.res" id="10_r32il"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dpkx1a780pvwv" path="res://assets/textures/tile_diamond.png" id="10_sx8rm"]
|
[ext_resource type="Texture2D" uid="uid://dpkx1a780pvwv" path="res://assets/textures/tile_diamond.png" id="10_sx8rm"]
|
||||||
|
[ext_resource type="ArrayMesh" uid="uid://bj4qr20oxos13" path="res://assets/models/meshes/freeze_floor.res" id="11_pgnbl"]
|
||||||
|
|
||||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5d0gc"]
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5d0gc"]
|
||||||
load_path = "res://.godot/imported/tile_heart.png-deeef50755ca225f028608dfd16900e6.s3tc.ctex"
|
load_path = "res://.godot/imported/tile_heart.png-deeef50755ca225f028608dfd16900e6.s3tc.ctex"
|
||||||
@@ -116,7 +117,8 @@ item/4/mesh_cast_shadow = 1
|
|||||||
item/4/shapes = []
|
item/4/shapes = []
|
||||||
item/4/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
item/4/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||||
item/4/navigation_layers = 1
|
item/4/navigation_layers = 1
|
||||||
item/5/name = "disable"
|
item/5/name = "freeze_floor"
|
||||||
|
item/5/mesh = ExtResource("11_pgnbl")
|
||||||
item/5/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
item/5/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||||
item/5/mesh_cast_shadow = 1
|
item/5/mesh_cast_shadow = 1
|
||||||
item/5/shapes = []
|
item/5/shapes = []
|
||||||
|
|||||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
[gd_resource type="ArrayMesh" load_steps=4 format=4 uid="uid://36tgon3b60db"]
|
[gd_resource type="ArrayMesh" format=4 uid="uid://36tgon3b60db"]
|
||||||
|
|
||||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5d0gc"]
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5d0gc"]
|
||||||
load_path = "res://.godot/imported/tile_heart.png-deeef50755ca225f028608dfd16900e6.s3tc.ctex"
|
load_path = "res://.godot/imported/tile_heart.png-deeef50755ca225f028608dfd16900e6.s3tc.ctex"
|
||||||
|
|||||||
+82
-19
@@ -57,6 +57,24 @@ func _ready():
|
|||||||
# Ensure grid is randomized with Scarcity if server
|
# Ensure grid is randomized with Scarcity if server
|
||||||
if multiplayer.is_server():
|
if multiplayer.is_server():
|
||||||
randomize_game_grid()
|
randomize_game_grid()
|
||||||
|
|
||||||
|
# Setup global multiplayer spawners (Stands, etc.)
|
||||||
|
_setup_multiplayer_spawners()
|
||||||
|
|
||||||
|
func _setup_multiplayer_spawners():
|
||||||
|
# Setup MultiplayerSpawner for Static Tekton Stands
|
||||||
|
# Create a container node for strict pathing
|
||||||
|
if not has_node("Stands"):
|
||||||
|
var stands_container = Node3D.new()
|
||||||
|
stands_container.name = "Stands"
|
||||||
|
add_child(stands_container)
|
||||||
|
|
||||||
|
if not has_node("StandSpawner"):
|
||||||
|
var stand_spawner = MultiplayerSpawner.new()
|
||||||
|
stand_spawner.name = "StandSpawner"
|
||||||
|
stand_spawner.spawn_path = NodePath("../Stands") # Relative to Spawner, finding sibling
|
||||||
|
stand_spawner.add_spawnable_scene("res://scenes/static_tekton_stand.tscn")
|
||||||
|
add_child(stand_spawner)
|
||||||
|
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func sync_portal_configs(configs: Array):
|
func sync_portal_configs(configs: Array):
|
||||||
@@ -70,18 +88,6 @@ func sync_portal_configs(configs: Array):
|
|||||||
var em = $EnhancedGridMap
|
var em = $EnhancedGridMap
|
||||||
if em:
|
if em:
|
||||||
em.cell_size = Vector3(1, 0.05, 1)
|
em.cell_size = Vector3(1, 0.05, 1)
|
||||||
|
|
||||||
# Setup MultiplayerSpawner for Static Tekton Stands
|
|
||||||
# Create a container node for strict pathing
|
|
||||||
var stands_container = Node3D.new()
|
|
||||||
stands_container.name = "Stands"
|
|
||||||
add_child(stands_container)
|
|
||||||
|
|
||||||
var stand_spawner = MultiplayerSpawner.new()
|
|
||||||
stand_spawner.name = "StandSpawner"
|
|
||||||
stand_spawner.spawn_path = NodePath("../Stands") # Relative to Spawner, finding sibling
|
|
||||||
stand_spawner.add_spawnable_scene("res://scenes/static_tekton_stand.tscn")
|
|
||||||
add_child(stand_spawner)
|
|
||||||
|
|
||||||
func _on_goal_count_updated(peer_id: int, count: int):
|
func _on_goal_count_updated(peer_id: int, count: int):
|
||||||
# Only update for local player
|
# Only update for local player
|
||||||
@@ -1588,8 +1594,30 @@ func sync_grid_item(x: int, y: int, z: int, item: int):
|
|||||||
if enhanced_gridmap.has_method("update_grid_data"):
|
if enhanced_gridmap.has_method("update_grid_data"):
|
||||||
enhanced_gridmap.update_grid_data()
|
enhanced_gridmap.update_grid_data()
|
||||||
|
|
||||||
# Sync grid update (no need to sync whole grid if we do it at start, but if we do it late we might need to sync)
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
# For simplicity, we trust the grid syncs via normal mechanisms or initial state.
|
func sync_grid_items_batch(data: Array):
|
||||||
|
# data is an array of dictionaries: [{x: int, y: int, z: int, item: int}, ...]
|
||||||
|
var enhanced_gridmap = $EnhancedGridMap
|
||||||
|
if not enhanced_gridmap:
|
||||||
|
return
|
||||||
|
|
||||||
|
for entry in data:
|
||||||
|
var x = entry.get("x", 0)
|
||||||
|
var y = entry.get("y", 0)
|
||||||
|
var z = entry.get("z", 0)
|
||||||
|
var item = entry.get("item", -1)
|
||||||
|
|
||||||
|
# WALL-SAFETY CHECK
|
||||||
|
if y == 1 and item >= 7 and item <= 20:
|
||||||
|
var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||||
|
if f0 == 4 or f0 == -1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
||||||
|
|
||||||
|
# Force visual update ONCE after batch
|
||||||
|
if enhanced_gridmap.has_method("update_grid_data"):
|
||||||
|
enhanced_gridmap.update_grid_data()
|
||||||
|
|
||||||
func randomize_game_grid():
|
func randomize_game_grid():
|
||||||
if LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors":
|
if LobbyManager.game_mode == "Stop n Go" or LobbyManager.game_mode == "Tekton Doors":
|
||||||
@@ -1762,7 +1790,7 @@ func sync_game_end_stop_n_go(winner_id: int):
|
|||||||
winner_name = player_node.display_name
|
winner_name = player_node.display_name
|
||||||
|
|
||||||
# Broadcast win (Validation already done in check_win_condition)
|
# Broadcast win (Validation already done in check_win_condition)
|
||||||
add_message_to_bar("MATCH COMPLETE", winner_name + " Wins with 3 Missions!", MessageType.GOAL)
|
add_message_to_bar("MATCH COMPLETE", winner_name + " Wins with 8 Missions!", MessageType.GOAL)
|
||||||
|
|
||||||
# Stop logic
|
# Stop logic
|
||||||
if stop_n_go_manager:
|
if stop_n_go_manager:
|
||||||
@@ -1800,10 +1828,42 @@ func _on_match_ended():
|
|||||||
var local_player = GameStateManager.local_player_character
|
var local_player = GameStateManager.local_player_character
|
||||||
if local_player:
|
if local_player:
|
||||||
local_player.action_points = 0
|
local_player.action_points = 0
|
||||||
|
|
||||||
|
# Signal Global Game End (Stops Bot ticks and logic)
|
||||||
|
GameStateManager.end_game()
|
||||||
|
|
||||||
|
# Freeze all game actors (Players, Bots, Tektons)
|
||||||
|
_freeze_all_game_actors()
|
||||||
|
|
||||||
# Show game over overlay
|
# Show game over overlay
|
||||||
_show_game_over_panel()
|
_show_game_over_panel()
|
||||||
|
|
||||||
|
func _freeze_all_game_actors():
|
||||||
|
"""Manually stop all game entities from processing without pausing the UI."""
|
||||||
|
print("[Main] Freezing all game actors recursively...")
|
||||||
|
|
||||||
|
var groups = ["Players", "Bots", "Tektons", "StaticTektonStands"]
|
||||||
|
for group_name in groups:
|
||||||
|
var nodes = get_tree().get_nodes_in_group(group_name)
|
||||||
|
for node in nodes:
|
||||||
|
_freeze_node_recursive(node)
|
||||||
|
|
||||||
|
func _freeze_node_recursive(node: Node):
|
||||||
|
"""Recursively disable processing and stop tweens for a node and its children."""
|
||||||
|
if node.has_method("set_physics_process"):
|
||||||
|
node.set_physics_process(false)
|
||||||
|
if node.has_method("set_process"):
|
||||||
|
node.set_process(false)
|
||||||
|
|
||||||
|
# Kill movement tweens if it's a character
|
||||||
|
if "_movement_tween" in node and node._movement_tween:
|
||||||
|
node._movement_tween.kill()
|
||||||
|
node._movement_tween = null
|
||||||
|
|
||||||
|
# Recursive call for all children
|
||||||
|
for child in node.get_children():
|
||||||
|
_freeze_node_recursive(child)
|
||||||
|
|
||||||
func _show_game_over_panel():
|
func _show_game_over_panel():
|
||||||
"""Create and display the game over panel with final leaderboard."""
|
"""Create and display the game over panel with final leaderboard."""
|
||||||
# Check if panel already exists
|
# Check if panel already exists
|
||||||
@@ -1818,6 +1878,9 @@ func _show_game_over_panel():
|
|||||||
|
|
||||||
if stop_n_go_manager and stop_n_go_manager.hud_layer:
|
if stop_n_go_manager and stop_n_go_manager.hud_layer:
|
||||||
stop_n_go_manager.hud_layer.hide()
|
stop_n_go_manager.hud_layer.hide()
|
||||||
|
|
||||||
|
if portal_mode_manager and portal_mode_manager.hud_layer:
|
||||||
|
portal_mode_manager.hud_layer.hide()
|
||||||
|
|
||||||
# Create game over panel
|
# Create game over panel
|
||||||
var panel = PanelContainer.new()
|
var panel = PanelContainer.new()
|
||||||
@@ -1829,7 +1892,7 @@ func _show_game_over_panel():
|
|||||||
style.bg_color = Color(0.0, 0.0, 0.0, 0.85)
|
style.bg_color = Color(0.0, 0.0, 0.0, 0.85)
|
||||||
panel.add_theme_stylebox_override("panel", style)
|
panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
# Content container
|
# CONTENT VBOX
|
||||||
var vbox = VBoxContainer.new()
|
var vbox = VBoxContainer.new()
|
||||||
vbox.name = "VBox"
|
vbox.name = "VBox"
|
||||||
vbox.set_anchors_preset(Control.PRESET_CENTER)
|
vbox.set_anchors_preset(Control.PRESET_CENTER)
|
||||||
@@ -2209,13 +2272,13 @@ func _toggle_pause_menu():
|
|||||||
var pause_menu = get_node_or_null("PauseMenu")
|
var pause_menu = get_node_or_null("PauseMenu")
|
||||||
if pause_menu:
|
if pause_menu:
|
||||||
pause_menu.visible = not pause_menu.visible
|
pause_menu.visible = not pause_menu.visible
|
||||||
get_tree().paused = pause_menu.visible
|
# get_tree().paused = pause_menu.visible # Removed for multiplayer consistency
|
||||||
|
|
||||||
func _on_resume_pressed():
|
func _on_resume_pressed():
|
||||||
var pause_menu = get_node_or_null("PauseMenu")
|
var pause_menu = get_node_or_null("PauseMenu")
|
||||||
if pause_menu:
|
if pause_menu:
|
||||||
pause_menu.visible = false
|
pause_menu.visible = false
|
||||||
get_tree().paused = false
|
# get_tree().paused = false # Removed for multiplayer consistency
|
||||||
|
|
||||||
func _on_how_to_play_pressed():
|
func _on_how_to_play_pressed():
|
||||||
var pause_menu = get_node_or_null("PauseMenu")
|
var pause_menu = get_node_or_null("PauseMenu")
|
||||||
@@ -2255,7 +2318,7 @@ func _on_settings_pressed():
|
|||||||
opacity_slider.set_value_no_signal(touch_controls.button_opacity)
|
opacity_slider.set_value_no_signal(touch_controls.button_opacity)
|
||||||
|
|
||||||
func _on_quit_match_pressed():
|
func _on_quit_match_pressed():
|
||||||
get_tree().paused = false
|
get_tree().paused = false # Ensure unpaused when returning to menu
|
||||||
# Properly disconnect from Nakama match
|
# Properly disconnect from Nakama match
|
||||||
_cleanup_multiplayer()
|
_cleanup_multiplayer()
|
||||||
# Return to lobby or main menu
|
# Return to lobby or main menu
|
||||||
|
|||||||
+10
-2
@@ -9978,6 +9978,7 @@ horizontal_alignment = 1
|
|||||||
[node name="TabContainer" type="TabContainer" parent="HowToPlayPanel/Panel/VBox" unique_id=123456794]
|
[node name="TabContainer" type="TabContainer" parent="HowToPlayPanel/Panel/VBox" unique_id=123456794]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
current_tab = 0
|
||||||
|
|
||||||
[node name="Free Mode" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456795]
|
[node name="Free Mode" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456795]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -9985,6 +9986,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 0
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Free Mode" unique_id=123456796]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Free Mode" unique_id=123456796]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10003,6 +10005,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 1
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Stop n Go" unique_id=123456798]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Stop n Go" unique_id=123456798]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10013,7 +10016,7 @@ text = "[b]Stop n Go[/b]
|
|||||||
- Move forward when the phase is [color=green][b]GO[/b][/color] (Green).
|
- Move forward when the phase is [color=green][b]GO[/b][/color] (Green).
|
||||||
- Stop completely when the phase is [color=red][b]STOP[/b][/color] (Red). Moving during a red phase will reset you to the start!
|
- Stop completely when the phase is [color=red][b]STOP[/b][/color] (Red). Moving during a red phase will reset you to the start!
|
||||||
- Your objective is to reach the mission tiles at the far end of the arena and safely carry them back to your starting zone.
|
- Your objective is to reach the mission tiles at the far end of the arena and safely carry them back to your starting zone.
|
||||||
- The first player to complete 3 missions wins."
|
- The first player to complete 8 missions and reach the finish floor wins."
|
||||||
|
|
||||||
[node name="Tekton Doors" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456799]
|
[node name="Tekton Doors" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456799]
|
||||||
visible = false
|
visible = false
|
||||||
@@ -10022,6 +10025,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 2
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Tekton Doors" unique_id=123456800]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Tekton Doors" unique_id=123456800]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10031,7 +10035,7 @@ text = "[b]Tekton Doors[/b]
|
|||||||
- Navigate a sprawling arena connected by color-coded portal doors.
|
- Navigate a sprawling arena connected by color-coded portal doors.
|
||||||
- Grab tiles and match goal patterns to earn mission completions.
|
- Grab tiles and match goal patterns to earn mission completions.
|
||||||
- Use doors to quickly teleport across rooms, but watch out for closures and traps.
|
- Use doors to quickly teleport across rooms, but watch out for closures and traps.
|
||||||
- The first player to complete 8 missions wins."
|
- The first player to complete 8 missions and reach the finish room wins."
|
||||||
|
|
||||||
[node name="Controls" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456805]
|
[node name="Controls" type="MarginContainer" parent="HowToPlayPanel/Panel/VBox/TabContainer" unique_id=123456805]
|
||||||
visible = false
|
visible = false
|
||||||
@@ -10040,6 +10044,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 3
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Controls" unique_id=123456806]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Controls" unique_id=123456806]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10061,6 +10066,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 4
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/The Grid" unique_id=123456808]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/The Grid" unique_id=123456808]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10079,6 +10085,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 5
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Tektons" unique_id=123456810]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Tektons" unique_id=123456810]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -10095,6 +10102,7 @@ theme_override_constants/margin_left = 10
|
|||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
metadata/_tab_index = 6
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Skills" unique_id=123456812]
|
[node name="RichTextLabel" type="RichTextLabel" parent="HowToPlayPanel/Panel/VBox/TabContainer/Skills" unique_id=123456812]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ func _spawn_portal_doors():
|
|||||||
const PORTAL_COLORS = [
|
const PORTAL_COLORS = [
|
||||||
Color(0, 1, 1), # Cyan
|
Color(0, 1, 1), # Cyan
|
||||||
Color(1, 0, 1), # Magenta
|
Color(1, 0, 1), # Magenta
|
||||||
Color(1, 1, 0), # Yellow
|
Color(1, 0, 0), # Red
|
||||||
Color(0, 1, 0), # Green
|
Color(0, 1, 0), # Green
|
||||||
Color(1, 0.5, 0) # Orange
|
Color(1, 0.5, 0) # Orange
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -328,37 +328,36 @@ func _execute_area_freeze(center_pos: Vector2i = Vector2i.ZERO):
|
|||||||
else:
|
else:
|
||||||
NotificationManager.send_message(player, "Hit %d Players!" % hit_count, NotificationManager.MessageType.GOAL)
|
NotificationManager.send_message(player, "Hit %d Players!" % hit_count, NotificationManager.MessageType.GOAL)
|
||||||
|
|
||||||
# Visual Feedback (Turn Floor Blue - Item 12 on Layer 0)
|
# Visual Feedback (Overlay Layer 2)
|
||||||
if player.is_multiplayer_authority():
|
if player.is_multiplayer_authority():
|
||||||
# Sync Icy Floor (Layer 0)
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
for x in range(-radius, radius + 1):
|
if main and main.has_method("sync_grid_items_batch"):
|
||||||
for y in range(-radius, radius + 1):
|
var batch_data = []
|
||||||
var pos = center_pos + Vector2i(x, y)
|
|
||||||
if enhanced_gridmap.is_position_valid(pos):
|
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
|
||||||
if main:
|
|
||||||
# CHECK: Don't overwrite Wall Block (Item 4)
|
|
||||||
var current_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y))
|
|
||||||
if current_item != 4: # 4 is Wall Block
|
|
||||||
# Use Item 12 (Blue Freeze Tile) on Layer 0 (Floor)
|
|
||||||
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
|
|
||||||
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(-radius, radius + 1):
|
for x in range(-radius, radius + 1):
|
||||||
for y in range(-radius, radius + 1):
|
for y in range(-radius, radius + 1):
|
||||||
var pos = center_pos + Vector2i(x, y)
|
var pos = center_pos + Vector2i(x, y)
|
||||||
if enhanced_gridmap.is_position_valid(pos):
|
if enhanced_gridmap.is_position_valid(pos):
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
# Use Item 5 (Freeze Floor) on Layer 2
|
||||||
if main:
|
batch_data.append({"x": pos.x, "y": 2, "z": pos.y, "item": 5})
|
||||||
# CHECK: Only restore if it is STILL Ice (Item 12)
|
|
||||||
# This prevents removing a Wall that was placed AFTER the freeze started
|
if not batch_data.is_empty():
|
||||||
var current_check = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y))
|
main.rpc("sync_grid_items_batch", batch_data)
|
||||||
if current_check == 12:
|
|
||||||
# Restore to Item 0 (Standard Floor)
|
# Cleanup visual timer (managed locally by author)
|
||||||
main.rpc("sync_grid_item", pos.x, 0, pos.y, 0)
|
get_tree().create_timer(FREEZE_SLOW_DURATION).timeout.connect(func():
|
||||||
)
|
var restore_batch = []
|
||||||
|
for x in range(-radius, radius + 1):
|
||||||
|
for y in range(-radius, radius + 1):
|
||||||
|
var pos = center_pos + Vector2i(x, y)
|
||||||
|
if enhanced_gridmap.is_position_valid(pos):
|
||||||
|
# Check if it is STILL Freeze Overlay
|
||||||
|
var current_check = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 2, pos.y))
|
||||||
|
if current_check == 5:
|
||||||
|
restore_batch.append({"x": pos.x, "y": 2, "z": pos.y, "item": -1})
|
||||||
|
|
||||||
|
if not restore_batch.is_empty():
|
||||||
|
main.rpc("sync_grid_items_batch", restore_batch)
|
||||||
|
)
|
||||||
|
|
||||||
func toggle_wall_orientation():
|
func toggle_wall_orientation():
|
||||||
wall_orientation_horizontal = !wall_orientation_horizontal
|
wall_orientation_horizontal = !wall_orientation_horizontal
|
||||||
@@ -384,41 +383,34 @@ func _execute_block_floor(target_pos: Vector2i):
|
|||||||
neighbors.append({"position": Vector2i(col_x, z)})
|
neighbors.append({"position": Vector2i(col_x, z)})
|
||||||
print("Player %s activated Wall Block: VERTICAL COLUMN (X=%d)" % [player.name, col_x])
|
print("Player %s activated Wall Block: VERTICAL COLUMN (X=%d)" % [player.name, col_x])
|
||||||
|
|
||||||
for n in neighbors:
|
if player.is_multiplayer_authority():
|
||||||
var pos = n.position
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
|
if main and main.has_method("sync_grid_items_batch"):
|
||||||
# PHYSICS CHECK
|
var batch_data = []
|
||||||
if _is_position_blocked_by_stand(pos):
|
for n in neighbors:
|
||||||
continue
|
var pos = n.position
|
||||||
|
if _is_position_blocked_by_stand(pos): continue
|
||||||
|
|
||||||
|
var block_pos = Vector3i(pos.x, 0, pos.y)
|
||||||
|
var current_item = enhanced_gridmap.get_cell_item(block_pos)
|
||||||
|
|
||||||
|
var is_immutable = false
|
||||||
|
if "immutable_items" in enhanced_gridmap:
|
||||||
|
if current_item in enhanced_gridmap.immutable_items:
|
||||||
|
is_immutable = true
|
||||||
|
if current_item == 4 or is_immutable: continue
|
||||||
|
|
||||||
|
batch_data.append({"x": block_pos.x, "y": 0, "z": block_pos.z, "item": 4})
|
||||||
|
|
||||||
|
# Record for restoration
|
||||||
|
blocked_tiles.append({
|
||||||
|
"position": block_pos,
|
||||||
|
"original_item": current_item,
|
||||||
|
"timer": BLOCK_DURATION
|
||||||
|
})
|
||||||
|
|
||||||
var block_pos = Vector3i(pos.x, 0, pos.y)
|
if not batch_data.is_empty():
|
||||||
|
main.rpc("sync_grid_items_batch", batch_data)
|
||||||
# Check current item first
|
|
||||||
var current_item = enhanced_gridmap.get_cell_item(block_pos)
|
|
||||||
|
|
||||||
# Skip if already a wall or immutable
|
|
||||||
# We assume Item 4 is the wall/stand.
|
|
||||||
# Also check enhanced_gridmap.immutable_items if available
|
|
||||||
var is_immutable = false
|
|
||||||
if "immutable_items" in enhanced_gridmap:
|
|
||||||
if current_item in enhanced_gridmap.immutable_items:
|
|
||||||
is_immutable = true
|
|
||||||
|
|
||||||
if current_item == 4 or is_immutable:
|
|
||||||
# Don't overwrite existing walls/stands, and don't schedule them for "restoration" (deletion)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if player.is_multiplayer_authority():
|
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
|
||||||
if main and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
|
|
||||||
main.rpc("sync_grid_item", block_pos.x, block_pos.y, block_pos.z, 4)
|
|
||||||
|
|
||||||
# Record for restoration
|
|
||||||
blocked_tiles.append({
|
|
||||||
"position": block_pos,
|
|
||||||
"original_item": current_item, # Restore the ACTUAL item that was there (e.g. ground 0 or maybe a dropped item?)
|
|
||||||
"timer": BLOCK_DURATION
|
|
||||||
})
|
|
||||||
|
|
||||||
# Notify
|
# Notify
|
||||||
var all_players = player.get_tree().get_nodes_in_group("Players")
|
var all_players = player.get_tree().get_nodes_in_group("Players")
|
||||||
@@ -471,6 +463,16 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
|||||||
|
|
||||||
var cell = Vector3i(pos.x, 1, pos.y)
|
var cell = Vector3i(pos.x, 1, pos.y)
|
||||||
|
|
||||||
|
# PREVENT SPAWNING ON FROZEN FLOORS (Visual/Lag Fix)
|
||||||
|
var is_frozen = false
|
||||||
|
if enhanced_gridmap:
|
||||||
|
# Check Layer 2 for Freeze Overlay (ID 5)
|
||||||
|
if enhanced_gridmap.get_cell_item(Vector3i(pos.x, 2, pos.y)) == 5:
|
||||||
|
is_frozen = true
|
||||||
|
|
||||||
|
if is_frozen:
|
||||||
|
continue
|
||||||
|
|
||||||
if player.is_multiplayer_authority():
|
if player.is_multiplayer_authority():
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
if main and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
|
if main and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
|
||||||
@@ -497,9 +499,9 @@ func _update_freeze_zones(delta: float):
|
|||||||
|
|
||||||
# If inside zone
|
# If inside zone
|
||||||
if dx <= zone.radius and dy <= zone.radius:
|
if dx <= zone.radius and dy <= zone.radius:
|
||||||
# Apply slow effect repeatedly
|
# Apply slow effect via RPC only IF not already slowed to prevent network flood
|
||||||
# We use a short duration so it expires quickly if they leave
|
if "movement_manager" in p and p.movement_manager and p.movement_manager.speed_multiplier >= 1.0:
|
||||||
p.rpc("apply_slow_effect", 0.5)
|
p.rpc("apply_slow_effect", 0.5)
|
||||||
|
|
||||||
if zone.timer <= 0:
|
if zone.timer <= 0:
|
||||||
zones_to_remove.append(i)
|
zones_to_remove.append(i)
|
||||||
@@ -523,7 +525,7 @@ func _check_for_icy_floor():
|
|||||||
return
|
return
|
||||||
|
|
||||||
var current_item = enhanced_gridmap.get_cell_item(Vector3i(player.current_position.x, 2, player.current_position.y))
|
var current_item = enhanced_gridmap.get_cell_item(Vector3i(player.current_position.x, 2, player.current_position.y))
|
||||||
if current_item == 15:
|
if current_item == 5: # Freeze Floor
|
||||||
_apply_slow_mo(player)
|
_apply_slow_mo(player)
|
||||||
elif player.movement_manager and player.movement_manager.speed_multiplier < 1.0:
|
elif player.movement_manager and player.movement_manager.speed_multiplier < 1.0:
|
||||||
# Check if we should restore speed
|
# Check if we should restore speed
|
||||||
@@ -581,7 +583,7 @@ func _create_restore_speed_timer(target_player: Node3D, duration: float):
|
|||||||
var still_in_zone = false
|
var still_in_zone = false
|
||||||
if enhanced_gridmap:
|
if enhanced_gridmap:
|
||||||
var item = enhanced_gridmap.get_cell_item(Vector3i(target_player.current_position.x, 2, target_player.current_position.y))
|
var item = enhanced_gridmap.get_cell_item(Vector3i(target_player.current_position.x, 2, target_player.current_position.y))
|
||||||
if item == 15:
|
if item == 5: # Freeze Floor
|
||||||
still_in_zone = true
|
still_in_zone = true
|
||||||
|
|
||||||
if not still_in_zone:
|
if not still_in_zone:
|
||||||
|
|||||||
Reference in New Issue
Block a user