Manually spawn weapons fixed
Pop the world weapon scene into weaponcontainer
This commit is contained in:
+38
-11
@@ -62,27 +62,54 @@ pickup_radius = 1.5
|
|||||||
weight = 2.0
|
weight = 2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Create a WorldWeapon Scene (Optional)
|
### Step 3: Spawn Weapons in the Level
|
||||||
|
|
||||||
If you want to place weapons in the level:
|
There are two ways to add weapons to your level:
|
||||||
|
|
||||||
```
|
#### Option A: Manual Placement (Recommended)
|
||||||
[RigidBody3D] - WorldWeaponYourWeapon
|
|
||||||
└─ [CollisionShape3D] - Approximate weapon shape
|
1. Open your level scene (e.g., `level/scenes/level.tscn`)
|
||||||
|
2. Drag a WorldWeapon scene into `WeaponsContainer`:
|
||||||
|
- From FileSystem panel: `level/scenes/weapons/world_weapon_sword.tscn`
|
||||||
|
- Or: `level/scenes/weapons/world_weapon_shield.tscn`
|
||||||
|
- Drop it as a child of `WeaponsContainer`
|
||||||
|
3. Position the weapon in 3D space where you want it to spawn
|
||||||
|
4. Save the scene
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- The server automatically detects manually placed WorldWeapon nodes on startup
|
||||||
|
- Each weapon is assigned a unique ID and tracked for multiplayer
|
||||||
|
- Clients automatically receive synced copies via RPC
|
||||||
|
- No code changes needed - just drag and drop!
|
||||||
|
|
||||||
|
#### Option B: Dynamic Spawning
|
||||||
|
|
||||||
|
Spawn weapons via code in `level.gd`:
|
||||||
|
|
||||||
|
```gdscript
|
||||||
|
# In _spawn_initial_weapons() or wherever you need
|
||||||
|
_weapon_spawn_counter += 1
|
||||||
|
rpc("spawn_world_weapon",
|
||||||
|
"res://level/resources/weapon_sword.tres",
|
||||||
|
Vector3(5, 1, 0), # spawn position
|
||||||
|
Vector3.ZERO, # initial velocity
|
||||||
|
_weapon_spawn_counter
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Set the script to `world_weapon.gd` and assign your WeaponData resource.
|
This is useful for:
|
||||||
|
- Spawning weapons at runtime
|
||||||
Example: `level/scenes/weapons/world_weapon_sword.tscn`
|
- Procedural weapon placement
|
||||||
|
- Loot drops from enemies
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
### In Godot Editor
|
### In Godot Editor
|
||||||
|
|
||||||
1. Open `level/scenes/level.tscn`
|
1. Open `level/scenes/level.tscn`
|
||||||
2. Drag a WorldWeapon scene (e.g., `world_weapon_sword.tscn`) into the level
|
2. Add a manually placed weapon (see Option A above) or modify `_spawn_initial_weapons()` in `level.gd`
|
||||||
3. Position it somewhere visible
|
3. Run the game (F5)
|
||||||
4. Run the game (F5)
|
4. Walk near the weapon and press **E** to pick it up
|
||||||
|
|
||||||
### Controls
|
### Controls
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[gd_resource type="Resource" script_class="WeaponData" load_steps=2 format=3 uid="uid://pqoldmf2q3t6"]
|
[gd_resource type="Resource" script_class="WeaponData" load_steps=3 format=3 uid="uid://pqoldmf2q3t6"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://d2homvlmrg6xs" path="res://level/scripts/weapon_data.gd" id="1"]
|
[ext_resource type="Script" uid="uid://d2homvlmrg6xs" path="res://level/scripts/weapon_data.gd" id="1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://rkvkbxlweo60" path="res://level/scenes/weapons/TestSwordMesh.tscn" id="1_gdc1w"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
@@ -10,4 +11,5 @@ damage = 20.0
|
|||||||
attack_range = 3.5
|
attack_range = 3.5
|
||||||
attack_cooldown = 0.6
|
attack_cooldown = 0.6
|
||||||
knockback_force = 12.0
|
knockback_force = 12.0
|
||||||
|
mesh_scene = ExtResource("1_gdc1w")
|
||||||
weight = 2.0
|
weight = 2.0
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
[gd_scene load_steps=18 format=3 uid="uid://dugaivbj1o66n"]
|
[gd_scene load_steps=19 format=3 uid="uid://dugaivbj1o66n"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://d0dgljwwl463n" path="res://level/scripts/level.gd" id="1_e1sh7"]
|
[ext_resource type="Script" uid="uid://d0dgljwwl463n" path="res://level/scripts/level.gd" id="1_e1sh7"]
|
||||||
[ext_resource type="PackedScene" uid="uid://db06e8q8f8bdq" path="res://level/scenes/Player_Lilguy.tscn" id="1_uvcbi"]
|
[ext_resource type="PackedScene" uid="uid://db06e8q8f8bdq" path="res://level/scenes/Player_Lilguy.tscn" id="1_uvcbi"]
|
||||||
[ext_resource type="FontFile" uid="uid://diapabmalpcrj" path="res://assets/fonts/Kurland.ttf" id="3_icc4p"]
|
[ext_resource type="FontFile" uid="uid://diapabmalpcrj" path="res://assets/fonts/Kurland.ttf" id="3_icc4p"]
|
||||||
[ext_resource type="PackedScene" uid="uid://b48oxbcgxu3d8" path="res://assets/Objects/Colosseum_10.fbx" id="4_u750a"]
|
[ext_resource type="PackedScene" uid="uid://b48oxbcgxu3d8" path="res://assets/Objects/Colosseum_10.fbx" id="4_u750a"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://hd6pq287rgye" path="res://level/scenes/weapons/world_weapon_testsword.tscn" id="5_cwx4m"]
|
||||||
|
|
||||||
[sub_resource type="PlaneMesh" id="PlaneMesh_r5xs5"]
|
[sub_resource type="PlaneMesh" id="PlaneMesh_r5xs5"]
|
||||||
size = Vector2(90, 90)
|
size = Vector2(90, 90)
|
||||||
@@ -346,6 +347,11 @@ offset_bottom = 40.0
|
|||||||
[node name="Colosseum_10" parent="." instance=ExtResource("4_u750a")]
|
[node name="Colosseum_10" parent="." instance=ExtResource("4_u750a")]
|
||||||
transform = Transform3D(15, 0, 0, 0, 15, 0, 0, 0, 15, 1.301034, -1.2294581, 2.0630608)
|
transform = Transform3D(15, 0, 0, 0, 15, 0, 0, 0, 15, 1.301034, -1.2294581, 2.0630608)
|
||||||
|
|
||||||
|
[node name="WeaponsContainer" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="WorldWeaponSword" parent="WeaponsContainer" instance=ExtResource("5_cwx4m")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.0268106, 2.6057472, 8.836907)
|
||||||
|
|
||||||
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Buttons/Host" to="." method="_on_host_pressed"]
|
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Buttons/Host" to="." method="_on_host_pressed"]
|
||||||
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Buttons/Join" to="." method="_on_join_pressed"]
|
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Buttons/Join" to="." method="_on_join_pressed"]
|
||||||
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Option4/Quit" to="." method="_on_quit_pressed"]
|
[connection signal="pressed" from="Menu/MainContainer/MainMenu/Option4/Quit" to="." method="_on_quit_pressed"]
|
||||||
|
|||||||
@@ -39,15 +39,81 @@ func _ready():
|
|||||||
add_child(weapons_container)
|
add_child(weapons_container)
|
||||||
print("Created WeaponsContainer")
|
print("Created WeaponsContainer")
|
||||||
|
|
||||||
|
# Clients: Remove manually placed weapons (server will sync them via RPC)
|
||||||
if not multiplayer.is_server():
|
if not multiplayer.is_server():
|
||||||
|
_cleanup_manual_weapons_on_client()
|
||||||
return
|
return
|
||||||
|
|
||||||
Network.connect("player_connected", Callable(self, "_on_player_connected"))
|
Network.connect("player_connected", Callable(self, "_on_player_connected"))
|
||||||
multiplayer.peer_disconnected.connect(_remove_player)
|
multiplayer.peer_disconnected.connect(_remove_player)
|
||||||
|
|
||||||
|
# Initialize any manually placed weapons in the scene
|
||||||
|
_initialize_manual_weapons()
|
||||||
|
|
||||||
# Spawn initial weapons when server starts
|
# Spawn initial weapons when server starts
|
||||||
_spawn_initial_weapons()
|
_spawn_initial_weapons()
|
||||||
|
|
||||||
|
func _cleanup_manual_weapons_on_client():
|
||||||
|
"""Remove manually placed weapons on clients (server will sync them via RPC)"""
|
||||||
|
if not weapons_container:
|
||||||
|
return
|
||||||
|
|
||||||
|
var weapons_to_remove = []
|
||||||
|
for child in weapons_container.get_children():
|
||||||
|
if child is WorldWeapon and child.weapon_id == -1:
|
||||||
|
weapons_to_remove.append(child)
|
||||||
|
|
||||||
|
for weapon in weapons_to_remove:
|
||||||
|
print("[Client] Removing manually placed weapon: ", weapon.name)
|
||||||
|
weapon.queue_free()
|
||||||
|
|
||||||
|
func _initialize_manual_weapons():
|
||||||
|
"""Initialize any WorldWeapon nodes manually placed in the level scene"""
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not weapons_container:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find all WorldWeapon nodes in the weapons container
|
||||||
|
var manual_weapons = []
|
||||||
|
for child in weapons_container.get_children():
|
||||||
|
if child is WorldWeapon:
|
||||||
|
manual_weapons.append(child)
|
||||||
|
|
||||||
|
if manual_weapons.is_empty():
|
||||||
|
print("[Server] No manually placed weapons found")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("[Server] Found ", manual_weapons.size(), " manually placed weapon(s)")
|
||||||
|
|
||||||
|
# Initialize each manually placed weapon
|
||||||
|
for weapon in manual_weapons:
|
||||||
|
# Skip if already initialized (weapon_id != -1)
|
||||||
|
if weapon.weapon_id != -1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Assign unique ID
|
||||||
|
_weapon_spawn_counter += 1
|
||||||
|
weapon.weapon_id = _weapon_spawn_counter
|
||||||
|
|
||||||
|
# Set deterministic name for networking
|
||||||
|
var old_name = weapon.name
|
||||||
|
weapon.name = "WorldWeapon_" + str(weapon.weapon_id)
|
||||||
|
|
||||||
|
# Track in active weapons
|
||||||
|
_active_weapons[weapon.weapon_id] = weapon
|
||||||
|
|
||||||
|
# Connect cleanup signal
|
||||||
|
weapon.tree_exiting.connect(_on_weapon_removed.bind(weapon.weapon_id))
|
||||||
|
|
||||||
|
print("[Server] Initialized manual weapon '", old_name, "' with ID: ", weapon.weapon_id, " at position: ", weapon.global_position)
|
||||||
|
|
||||||
|
# Verify weapon_data is set
|
||||||
|
if not weapon.weapon_data:
|
||||||
|
push_error("Manual weapon '", old_name, "' has no WeaponData assigned!")
|
||||||
|
continue
|
||||||
|
|
||||||
func _spawn_initial_weapons():
|
func _spawn_initial_weapons():
|
||||||
if not multiplayer.is_server():
|
if not multiplayer.is_server():
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ var _pickup_area: Area3D = null
|
|||||||
var _is_being_picked_up: bool = false
|
var _is_being_picked_up: bool = false
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
print("[WorldWeapon ", name, "] _ready() called. weapon_id=", weapon_id, ", weapon_data=", weapon_data)
|
||||||
|
|
||||||
# Set collision layer to "weapon" (layer 3 = bit 4)
|
# Set collision layer to "weapon" (layer 3 = bit 4)
|
||||||
collision_layer = 4
|
collision_layer = 4
|
||||||
collision_mask = 2 # Collide with world
|
collision_mask = 2 # Collide with world
|
||||||
@@ -21,10 +23,14 @@ func _ready():
|
|||||||
# Set up physics
|
# Set up physics
|
||||||
if weapon_data:
|
if weapon_data:
|
||||||
mass = weapon_data.weight
|
mass = weapon_data.weight
|
||||||
|
print("[WorldWeapon ", name, "] Set mass to ", mass)
|
||||||
|
|
||||||
# Spawn mesh
|
# Spawn mesh
|
||||||
if weapon_data and weapon_data.mesh_scene:
|
if weapon_data and weapon_data.mesh_scene:
|
||||||
|
print("[WorldWeapon ", name, "] Spawning mesh from ", weapon_data.mesh_scene)
|
||||||
_spawn_mesh()
|
_spawn_mesh()
|
||||||
|
else:
|
||||||
|
print("[WorldWeapon ", name, "] ERROR: Cannot spawn mesh. weapon_data=", weapon_data, ", mesh_scene=", weapon_data.mesh_scene if weapon_data else "null")
|
||||||
|
|
||||||
# Create collision shape if not exists
|
# Create collision shape if not exists
|
||||||
_setup_collision()
|
_setup_collision()
|
||||||
@@ -44,6 +50,7 @@ func _spawn_mesh():
|
|||||||
# Instantiate mesh
|
# Instantiate mesh
|
||||||
_mesh_instance = weapon_data.mesh_scene.instantiate()
|
_mesh_instance = weapon_data.mesh_scene.instantiate()
|
||||||
add_child(_mesh_instance)
|
add_child(_mesh_instance)
|
||||||
|
print("[WorldWeapon ", name, "] Mesh spawned successfully: ", _mesh_instance)
|
||||||
|
|
||||||
func _setup_collision():
|
func _setup_collision():
|
||||||
# Check if we already have a collision shape
|
# Check if we already have a collision shape
|
||||||
|
|||||||
Reference in New Issue
Block a user