Manually spawn weapons fixed

Pop the world weapon scene into weaponcontainer
Dashfix
Twirpytherobot 3 weeks ago
parent 3431c12dc2
commit 38994776df
  1. 53
      WEAPON_SYSTEM.md
  2. 4
      level/resources/weapon_testsword.tres
  3. 8
      level/scenes/level.tscn
  4. 66
      level/scripts/level.gd
  5. 7
      level/scripts/world_weapon.gd

@ -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)
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
)
``` ```
[RigidBody3D] - WorldWeaponYourWeapon
└─ [CollisionShape3D] - Approximate weapon shape
```
Set the script to `world_weapon.gd` and assign your WeaponData resource.
Example: `level/scenes/weapons/world_weapon_sword.tscn` This is useful for:
- Spawning weapons at runtime
- 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

Loading…
Cancel
Save