feat: Add EnhancedGridMap for dynamic grid generation, pathfinding, and network synchronization, and introduce core game scripts for Tekton entities and managers.
This commit is contained in:
+88
-41
@@ -55,6 +55,18 @@ func _ready():
|
||||
if em:
|
||||
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):
|
||||
# Only update for local player
|
||||
if peer_id == multiplayer.get_unique_id():
|
||||
@@ -699,61 +711,96 @@ func spawn_static_tektons():
|
||||
# ID: 99000 + i (Consistent IDs for Static Tektons)
|
||||
var id = 99000 + i
|
||||
|
||||
# Pick Shape on Server (0:Cyl, 1:Box, 2:Prism, 3:Sphere)
|
||||
var shape_idx = randi() % 4
|
||||
|
||||
# Spawn on Server
|
||||
_create_static_setup(pos, id)
|
||||
_create_static_setup(pos, id, shape_idx)
|
||||
|
||||
# Sync to Clients
|
||||
rpc("sync_spawn_static_setup", pos, id)
|
||||
rpc("sync_spawn_static_setup", pos, id, shape_idx)
|
||||
|
||||
@rpc("call_remote", "reliable")
|
||||
func sync_spawn_static_setup(pos: Vector2i, tekton_id: int):
|
||||
_create_static_setup(pos, tekton_id)
|
||||
@rpc("call_local", "reliable")
|
||||
func sync_spawn_static_setup(pos: Vector2i, tekton_id: int, shape_idx: int):
|
||||
# Call local creation logic on all peers.
|
||||
# Server: Spawns Stand + Void + Tekton
|
||||
# Client: Avoids Stand (Spawner) + Void + Tekton
|
||||
_create_static_setup(pos, tekton_id, shape_idx)
|
||||
|
||||
func _create_static_setup(pos: Vector2i, tekton_id: int):
|
||||
func _create_static_setup(pos: Vector2i, tekton_id: int, shape_idx: int):
|
||||
"""Creates both the Stand and the Static Tekton at the position."""
|
||||
var enhanced_gridmap = $EnhancedGridMap
|
||||
|
||||
# 1. Create Stand
|
||||
var stand_name = "StaticStand_%d" % tekton_id
|
||||
if not has_node(stand_name):
|
||||
var stand_scene = load("res://scenes/static_tekton_stand.tscn")
|
||||
if stand_scene:
|
||||
var stand = stand_scene.instantiate()
|
||||
stand.name = stand_name
|
||||
add_child(stand)
|
||||
# 1. Create Stand (Server Only - Synced via Spawner)
|
||||
# IMPORTANT: Clients receive the Stand via MultiplayerSpawner.
|
||||
# They MUST NOT spawn it manually here or we get duplicates.
|
||||
if multiplayer.is_server():
|
||||
var stands_container = get_node_or_null("Stands")
|
||||
if stands_container:
|
||||
var stand_name = "StaticStand_%d" % tekton_id
|
||||
if not stands_container.has_node(stand_name):
|
||||
var stand_scene = load("res://scenes/static_tekton_stand.tscn")
|
||||
if stand_scene:
|
||||
var stand = stand_scene.instantiate()
|
||||
stand.name = stand_name
|
||||
|
||||
# Set Shape Index BEFORE adding to tree (so _ready picks it up/syncs)
|
||||
if "shape_index" in stand:
|
||||
stand.shape_index = shape_idx
|
||||
|
||||
stands_container.add_child(stand)
|
||||
|
||||
# Position Stand
|
||||
if enhanced_gridmap:
|
||||
# Convert grid to world
|
||||
var world_pos = Vector3(pos.x + 0.5, 0, pos.y + 0.5)
|
||||
if "cell_size" in enhanced_gridmap:
|
||||
world_pos = Vector3(
|
||||
pos.x * enhanced_gridmap.cell_size.x + enhanced_gridmap.cell_size.x/2,
|
||||
0,
|
||||
pos.y * enhanced_gridmap.cell_size.z + enhanced_gridmap.cell_size.z/2
|
||||
)
|
||||
stand.global_position = world_pos
|
||||
|
||||
# 2. Modify Base (Void) - Runs on ALL peers to update local GridMap visual/collision
|
||||
if enhanced_gridmap:
|
||||
var floor_count = 3
|
||||
if "floors" in enhanced_gridmap:
|
||||
floor_count = enhanced_gridmap.floors
|
||||
|
||||
# Position Stand
|
||||
if enhanced_gridmap:
|
||||
# Convert grid to world
|
||||
var world_pos = Vector3(pos.x + 0.5, 0, pos.y + 0.5)
|
||||
if "cell_size" in enhanced_gridmap:
|
||||
world_pos = Vector3(
|
||||
pos.x * enhanced_gridmap.cell_size.x + enhanced_gridmap.cell_size.x/2,
|
||||
0,
|
||||
pos.y * enhanced_gridmap.cell_size.z + enhanced_gridmap.cell_size.z/2
|
||||
)
|
||||
stand.global_position = world_pos
|
||||
for dx in range(-1, 2):
|
||||
for dy in range(-1, 2):
|
||||
var tile_pos_x = pos.x + dx
|
||||
var tile_pos_z = pos.y + dy
|
||||
|
||||
# Update GridMap to block pathfinding (Item 4 = Wall)
|
||||
# Mark entire 3x3 area as immutable obstacles on FLOOR 0 (Ground Level)
|
||||
# This overwrites the ground tile to ensure PlayerMovementManager sees it as blocked.
|
||||
for dx in range(-1, 2):
|
||||
for dy in range(-1, 2):
|
||||
var tile_pos = Vector3i(pos.x + dx, 0, pos.y + dy)
|
||||
enhanced_gridmap.set_cell_item(tile_pos, 4)
|
||||
|
||||
# CRITICAL: Force AStar update so Bots and Pathfinding know about the new walls
|
||||
if enhanced_gridmap.has_method("update_astar_costs"):
|
||||
enhanced_gridmap.update_astar_costs()
|
||||
# Clear ALL vertical layers (Ground, Items, etc.)
|
||||
for f in range(floor_count):
|
||||
var tile_pos = Vector3i(tile_pos_x, f, tile_pos_z)
|
||||
enhanced_gridmap.set_cell_item(tile_pos, -1) # -1 = Empty/Void
|
||||
|
||||
# CRITICAL: Force AStar update so Bots and Pathfinding know about the new walls
|
||||
if enhanced_gridmap.has_method("update_astar_costs"):
|
||||
enhanced_gridmap.update_astar_costs()
|
||||
|
||||
# 2. Create Tekton
|
||||
# Reuse _create_tekton logic but force params
|
||||
_create_tekton(pos, tekton_id, true)
|
||||
# 3. Create Tekton Visual - Runs on ALL peers
|
||||
# NOTE: Tekton NPC is currently not managed by a specialized Spawner for static setup?
|
||||
# Or it is? If _create_tekton adds it to a path watched by a spawner, we should duplicate check.
|
||||
# _create_tekton instantiates 'tekton.tscn' and adds to 'Main'.
|
||||
# Main usually has a MultiplayerSpawner for 'Players' etc., but let's check.
|
||||
# The original logic spawned it everywhere, so we keep that behavior to be safe.
|
||||
# But we add a check to avoid duplicates if it already came in via sync.
|
||||
if not has_node("Tekton_%d" % tekton_id):
|
||||
_create_tekton(pos, tekton_id, true)
|
||||
|
||||
# Force Tekton height UP to sit on stand
|
||||
# Force Tekton height UP to sit on stand on ALL peers
|
||||
var tekton = get_node_or_null("Tekton_%d" % tekton_id)
|
||||
if tekton:
|
||||
tekton.position.y += 0.6 # Stand Height
|
||||
var height_offset = 0.6
|
||||
# If Sphere (Index 3), it is taller (Dome)
|
||||
if shape_idx == 3:
|
||||
height_offset = 1.3
|
||||
|
||||
tekton.position.y += height_offset
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://static_tekton_stand_001"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://static_tekton_stand_001"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/static_tekton_stand.gd" id="1_script"]
|
||||
|
||||
[sub_resource type="CylinderMesh" id="CylinderMesh_stand"]
|
||||
top_radius = 1.4
|
||||
@@ -10,12 +12,23 @@ albedo_color = Color(0.15, 0.15, 0.2, 1)
|
||||
metallic = 0.6
|
||||
roughness = 0.4
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_stand"]
|
||||
height = 0.6
|
||||
radius = 1.4
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_stand"]
|
||||
size = Vector3(3.2, 0.6, 3.2)
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_stand"]
|
||||
properties/0/path = NodePath(":shape_index")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 2
|
||||
properties/1/path = NodePath(":global_position")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 2
|
||||
|
||||
[node name="StaticTektonStand" type="StaticBody3D"]
|
||||
collision_mask = 0
|
||||
script = ExtResource("1_script")
|
||||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_stand")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
|
||||
@@ -24,4 +37,4 @@ surface_material_override/0 = SubResource("StandardMaterial3D_stand")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
|
||||
shape = SubResource("CylinderShape3D_stand")
|
||||
shape = SubResource("BoxShape3D_stand")
|
||||
|
||||
Reference in New Issue
Block a user