New player model with anims!!!!!!
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(godot:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://db06e8q8f8bdq"]
|
[gd_scene load_steps=6 format=3 uid="uid://db06e8q8f8bdq"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://byw3ig2bs1wgu" path="res://assets/characters/player/LilguyRigged.glb" id="1_e6qwr"]
|
[ext_resource type="PackedScene" uid="uid://byw3ig2bs1wgu" path="res://assets/characters/player/LilguyRigged.glb" id="1_e6qwr"]
|
||||||
|
[ext_resource type="Script" uid="uid://cf7jky1bcs560" path="res://level/scripts/lilguy_body.gd" id="3_body"]
|
||||||
[ext_resource type="Script" uid="uid://bj7yrijm7bppq" path="res://level/scripts/spring_arm_offset.gd" id="9_dlyie"]
|
[ext_resource type="Script" uid="uid://bj7yrijm7bppq" path="res://level/scripts/spring_arm_offset.gd" id="9_dlyie"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
||||||
@@ -11,20 +12,24 @@ height = 1.73092
|
|||||||
properties/0/path = NodePath(".:position")
|
properties/0/path = NodePath(".:position")
|
||||||
properties/0/spawn = true
|
properties/0/spawn = true
|
||||||
properties/0/replication_mode = 1
|
properties/0/replication_mode = 1
|
||||||
properties/1/path = NodePath("3DGodotRobot/AnimationPlayer:current_animation")
|
properties/1/path = NodePath("AnimationPlayer:current_animation")
|
||||||
properties/1/spawn = true
|
properties/1/spawn = true
|
||||||
properties/1/replication_mode = 1
|
properties/1/replication_mode = 1
|
||||||
properties/2/path = NodePath("PlayerNick/Nickname:text")
|
properties/2/path = NodePath("PlayerNick/Nickname:text")
|
||||||
properties/2/spawn = true
|
properties/2/spawn = true
|
||||||
properties/2/replication_mode = 1
|
properties/2/replication_mode = 1
|
||||||
properties/3/path = NodePath("3DGodotRobot:rotation")
|
properties/3/path = NodePath("Armature:rotation")
|
||||||
properties/3/spawn = true
|
properties/3/spawn = true
|
||||||
properties/3/replication_mode = 1
|
properties/3/replication_mode = 1
|
||||||
|
|
||||||
[node name="LilguyRigged" instance=ExtResource("1_e6qwr")]
|
[node name="LilguyRigged" instance=ExtResource("1_e6qwr")]
|
||||||
|
collision_mask = 3
|
||||||
|
|
||||||
[node name="Armature" parent="." index="0"]
|
[node name="Armature" parent="." index="0" node_paths=PackedStringArray("_character", "animation_player")]
|
||||||
transform = Transform3D(0.003, 0, 0, 0, -1.3113416e-10, -0.003, 0, 0.003, -1.3113416e-10, 0, 0, 0)
|
transform = Transform3D(0.003, 0, 0, 0, -1.3113416e-10, -0.003, 0, 0.003, -1.3113416e-10, 0, 0, 0)
|
||||||
|
script = ExtResource("3_body")
|
||||||
|
_character = NodePath("..")
|
||||||
|
animation_player = NodePath("../AnimationPlayer")
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Armature" index="0"]
|
[node name="Skeleton3D" parent="Armature" index="0"]
|
||||||
bones/0/position = Vector3(-0.32852697, 2.914154, -546.76843)
|
bones/0/position = Vector3(-0.32852697, 2.914154, -546.76843)
|
||||||
@@ -41,38 +46,54 @@ bones/5/position = Vector3(-0.25257444, 72.84532, -7.644296e-06)
|
|||||||
bones/5/rotation = Quaternion(0.03735582, 0.19943666, -0.04159315, 0.97831464)
|
bones/5/rotation = Quaternion(0.03735582, 0.19943666, -0.04159315, 0.97831464)
|
||||||
bones/6/position = Vector3(-0.606337, 174.89494, 7.152558e-06)
|
bones/6/position = Vector3(-0.606337, 174.89494, 7.152558e-06)
|
||||||
bones/7/position = Vector3(-0.19949026, 76.75483, 52.286175)
|
bones/7/position = Vector3(-0.19949026, 76.75483, 52.286175)
|
||||||
bones/7/rotation = Quaternion(0.8036073, -0.09628728, 0.10672547, 0.57754105)
|
bones/7/rotation = Quaternion(0.8036073, -0.09628729, 0.106725484, 0.57754105)
|
||||||
bones/8/position = Vector3(4.5403274e-05, 110.91907, 9.404198e-05)
|
bones/8/position = Vector3(4.5403274e-05, 110.91907, 9.404198e-05)
|
||||||
bones/8/rotation = Quaternion(0.25522038, -0.08967137, 0.029357873, 0.962268)
|
bones/8/rotation = Quaternion(0.25522032, -0.08967137, 0.029357875, 0.962268)
|
||||||
bones/9/position = Vector3(2.3064584e-05, 173.66367, 5.063071e-05)
|
bones/9/position = Vector3(2.3064584e-05, 173.66367, 5.063071e-05)
|
||||||
bones/9/rotation = Quaternion(0.0878422, -0.16096674, 0.2433837, 0.9524378)
|
bones/9/rotation = Quaternion(0.0878422, -0.16096674, 0.2433837, 0.9524378)
|
||||||
bones/10/position = Vector3(-2.2947788e-05, 166.48767, -1.2734416e-05)
|
bones/10/position = Vector3(-2.2947788e-05, 166.48767, -1.2734416e-05)
|
||||||
bones/11/position = Vector3(0.23053212, 76.75536, -52.28617)
|
bones/11/position = Vector3(0.23053212, 76.75536, -52.28617)
|
||||||
bones/11/rotation = Quaternion(0.14271575, -0.5852634, 0.78228706, 0.15851216)
|
bones/11/rotation = Quaternion(0.14271575, -0.5852634, 0.782287, 0.15851216)
|
||||||
bones/12/position = Vector3(1.532285e-05, 110.91911, 4.0430357e-05)
|
bones/12/position = Vector3(1.532285e-05, 110.91911, 4.0430357e-05)
|
||||||
bones/12/rotation = Quaternion(0.3219699, 0.13412467, 0.27112576, 0.8971269)
|
bones/12/rotation = Quaternion(0.32196987, 0.13412467, 0.27112576, 0.8971269)
|
||||||
bones/13/position = Vector3(1.5523525e-05, 173.6661, 0.00010698747)
|
bones/13/position = Vector3(1.5523525e-05, 173.6661, 0.00010698747)
|
||||||
bones/13/rotation = Quaternion(0.090376474, 0.101556264, -0.39819276, 0.907172)
|
bones/13/rotation = Quaternion(0.09037647, 0.101556264, -0.39819276, 0.90717196)
|
||||||
bones/14/position = Vector3(-2.0682812e-05, 166.48976, 3.939679e-05)
|
bones/14/position = Vector3(-2.0682812e-05, 166.48976, 3.939679e-05)
|
||||||
bones/15/position = Vector3(0.6496186, -35.1185, 49.84838)
|
bones/15/position = Vector3(0.6496186, -35.1185, 49.84838)
|
||||||
bones/15/rotation = Quaternion(0.38543195, 0.163806, 0.8217493, 0.3864424)
|
bones/15/rotation = Quaternion(0.38543195, 0.163806, 0.8217493, 0.3864424)
|
||||||
bones/16/position = Vector3(8.771768e-06, 312.91962, 7.4840264e-06)
|
bones/16/position = Vector3(8.771768e-06, 312.91962, 7.4840264e-06)
|
||||||
bones/16/rotation = Quaternion(-0.05300442, 0.17209676, 0.3905684, 0.90278995)
|
bones/16/rotation = Quaternion(-0.05300442, 0.17209676, 0.3905684, 0.90278995)
|
||||||
bones/17/position = Vector3(-1.8137518e-05, 301.05597, -2.1670077e-05)
|
bones/17/position = Vector3(-1.8137518e-05, 301.05597, -2.1670077e-05)
|
||||||
bones/17/rotation = Quaternion(0.24982397, 0.64725155, -0.6711768, 0.2611037)
|
bones/17/rotation = Quaternion(0.24982396, 0.64725155, -0.6711768, 0.2611037)
|
||||||
bones/18/position = Vector3(-3.026353e-05, 14.185886, -1.4917823e-06)
|
bones/18/position = Vector3(-3.026353e-05, 14.185886, -1.4917823e-06)
|
||||||
bones/18/rotation = Quaternion(0.11539763, 0.017187236, -0.010002268, 0.9931203)
|
bones/18/rotation = Quaternion(0.11539763, 0.017187234, -0.0100022685, 0.9931203)
|
||||||
bones/19/position = Vector3(-4.351055e-06, 11.391233, -2.5032205e-06)
|
bones/19/position = Vector3(-4.351055e-06, 11.391233, -2.5032205e-06)
|
||||||
bones/20/position = Vector3(0.014209064, -35.118507, -49.848385)
|
bones/20/position = Vector3(0.014209064, -35.118507, -49.848385)
|
||||||
bones/20/rotation = Quaternion(-0.07370265, -0.18747172, 0.9412239, 0.2711456)
|
bones/20/rotation = Quaternion(-0.07370265, -0.18747172, 0.9412239, 0.2711456)
|
||||||
bones/21/position = Vector3(2.8756085e-05, 312.91974, 5.14377e-06)
|
bones/21/position = Vector3(2.8756085e-05, 312.91974, 5.14377e-06)
|
||||||
bones/21/rotation = Quaternion(-0.037353612, -0.04220169, 0.46135134, 0.8854257)
|
bones/21/rotation = Quaternion(-0.03735361, -0.04220169, 0.46135134, 0.8854257)
|
||||||
bones/22/position = Vector3(2.2092872e-05, 301.0575, 1.8114511e-05)
|
bones/22/position = Vector3(2.2092872e-05, 301.0575, 1.8114511e-05)
|
||||||
bones/22/rotation = Quaternion(0.7933202, 0.12858748, -0.36227232, 0.47208825)
|
bones/22/rotation = Quaternion(0.7933202, 0.12858748, -0.3622723, 0.47208822)
|
||||||
bones/23/position = Vector3(1.3624241e-05, 15.034077, 9.790485e-06)
|
bones/23/position = Vector3(1.3624241e-05, 15.034077, 9.790485e-06)
|
||||||
bones/23/rotation = Quaternion(0.11885707, 0.00952176, -0.0077993367, 0.9928351)
|
bones/23/rotation = Quaternion(0.11885707, 0.009521758, -0.0077993367, 0.9928351)
|
||||||
bones/24/position = Vector3(-2.4847686e-06, 11.913359, -6.198885e-06)
|
bones/24/position = Vector3(-2.4847686e-06, 11.913359, -6.198885e-06)
|
||||||
|
|
||||||
|
[node name="WeaponPoint" type="BoneAttachment3D" parent="Armature/Skeleton3D" index="1"]
|
||||||
|
transform = Transform3D(-0.43292555, -0.61284775, 0.6610542, 0.7782953, 0.11585887, 0.61711675, -0.45478758, 0.78166103, 0.42681834, -352.38528, -73.5694, -531.96124)
|
||||||
|
bone_name = "mixamorig_RightHand"
|
||||||
|
bone_idx = 14
|
||||||
|
|
||||||
|
[node name="WeaponContainer" type="Node3D" parent="Armature/Skeleton3D/WeaponPoint" index="0"]
|
||||||
|
transform = Transform3D(36.6912, 297.2667, 16.921356, 46.72698, 11.0892515, -296.13126, -294.05847, 38.85366, -44.94499, 24.08223, -7.4241333, 7.098694)
|
||||||
|
|
||||||
|
[node name="OffhandPoint" type="BoneAttachment3D" parent="Armature/Skeleton3D" index="2"]
|
||||||
|
transform = Transform3D(0.6212382, -0.004605584, -0.7836083, -0.620316, 0.60813713, -0.49535576, 0.47882265, 0.7938187, 0.37494114, 135.65903, 334.35764, -511.2708)
|
||||||
|
bone_name = "mixamorig_LeftHand"
|
||||||
|
bone_idx = 10
|
||||||
|
|
||||||
|
[node name="OffhandContainer" type="Node3D" parent="Armature/Skeleton3D/OffhandPoint" index="0"]
|
||||||
|
transform = Transform3D(-17.74905, -295.46814, -48.82108, 21.019196, -50.01525, 295.05362, -298.73593, 14.035805, 23.660797, 0.005859375, 0.39337158, 0.06616211)
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." index="2"]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="." index="2"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.066, 0.828, 0.01)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.066, 0.828, 0.01)
|
||||||
shape = SubResource("CapsuleShape3D_yxyay")
|
shape = SubResource("CapsuleShape3D_yxyay")
|
||||||
|
|||||||
+2
-10
@@ -1,11 +1,9 @@
|
|||||||
[gd_scene load_steps=20 format=3 uid="uid://dugaivbj1o66n"]
|
[gd_scene load_steps=18 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://cffjduipbb3s5" path="res://level/scenes/player.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" path="res://level/scenes/weapons/world_weapon_shield.tscn" id="5_xerh7"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://hd6pq287rgye" path="res://level/scenes/weapons/world_weapon_testsword.tscn" id="6_xerh7"]
|
|
||||||
|
|
||||||
[sub_resource type="PlaneMesh" id="PlaneMesh_r5xs5"]
|
[sub_resource type="PlaneMesh" id="PlaneMesh_r5xs5"]
|
||||||
size = Vector2(90, 90)
|
size = Vector2(90, 90)
|
||||||
@@ -348,12 +346,6 @@ 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="WorldWeaponShield" parent="." instance=ExtResource("5_xerh7")]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.6566563, 0.024999619, 8.476057)
|
|
||||||
|
|
||||||
[node name="WorldWeaponSword" parent="." instance=ExtResource("6_xerh7")]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.1038065, 0.024999619, 4.551466)
|
|
||||||
|
|
||||||
[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"]
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ func perform_attack() -> bool:
|
|||||||
if owner_character._body:
|
if owner_character._body:
|
||||||
owner_character._body.play_attack()
|
owner_character._body.play_attack()
|
||||||
|
|
||||||
# Find targets in range
|
# Delay damage until animation hits (roughly 70% through the animation)
|
||||||
_find_and_damage_targets()
|
# This makes the damage apply when the swing actually connects
|
||||||
|
var damage_delay = weapon_data.attack_cooldown * 0.4 # Adjust this multiplier to change when damage happens
|
||||||
|
get_tree().create_timer(damage_delay).timeout.connect(_find_and_damage_targets)
|
||||||
|
|
||||||
attack_performed.emit()
|
attack_performed.emit()
|
||||||
return true
|
return true
|
||||||
|
|||||||
+94
-9
@@ -11,6 +11,8 @@ extends Node3D
|
|||||||
|
|
||||||
# Weapon spawning counter (server-side only)
|
# Weapon spawning counter (server-side only)
|
||||||
var _weapon_spawn_counter: int = 0
|
var _weapon_spawn_counter: int = 0
|
||||||
|
# Track active weapons for late-join sync (server-side only)
|
||||||
|
var _active_weapons: Dictionary = {} # weapon_id -> WorldWeapon reference
|
||||||
|
|
||||||
# multiplayer chat
|
# multiplayer chat
|
||||||
@onready var message: LineEdit = $MultiplayerChat/VBoxContainer/HBoxContainer/Message
|
@onready var message: LineEdit = $MultiplayerChat/VBoxContainer/HBoxContainer/Message
|
||||||
@@ -53,8 +55,11 @@ func _spawn_initial_weapons():
|
|||||||
# Wait a frame for everything to be ready
|
# Wait a frame for everything to be ready
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
|
|
||||||
|
print("[Server] _spawn_initial_weapons - Connected peers: ", multiplayer.get_peers())
|
||||||
|
|
||||||
# Spawn a sword
|
# Spawn a sword
|
||||||
_weapon_spawn_counter += 1
|
_weapon_spawn_counter += 1
|
||||||
|
print("[Server] Calling RPC to spawn sword with ID: ", _weapon_spawn_counter)
|
||||||
rpc("spawn_world_weapon",
|
rpc("spawn_world_weapon",
|
||||||
"res://level/resources/weapon_sword.tres",
|
"res://level/resources/weapon_sword.tres",
|
||||||
Vector3(5, 1, 0),
|
Vector3(5, 1, 0),
|
||||||
@@ -64,6 +69,7 @@ func _spawn_initial_weapons():
|
|||||||
|
|
||||||
# Spawn a shield
|
# Spawn a shield
|
||||||
_weapon_spawn_counter += 1
|
_weapon_spawn_counter += 1
|
||||||
|
print("[Server] Calling RPC to spawn shield with ID: ", _weapon_spawn_counter)
|
||||||
rpc("spawn_world_weapon",
|
rpc("spawn_world_weapon",
|
||||||
"res://level/resources/weapon_shield.tres",
|
"res://level/resources/weapon_shield.tres",
|
||||||
Vector3(-5, 1, 0),
|
Vector3(-5, 1, 0),
|
||||||
@@ -72,13 +78,24 @@ func _spawn_initial_weapons():
|
|||||||
)
|
)
|
||||||
|
|
||||||
func _on_player_connected(peer_id, player_info):
|
func _on_player_connected(peer_id, player_info):
|
||||||
# for id in Network.players.keys():
|
|
||||||
# var player_data = Network.players[id]
|
|
||||||
# if id != peer_id:
|
|
||||||
# rpc_id(peer_id, "sync_player_skin", id, player_data["skin"])
|
|
||||||
|
|
||||||
_add_player(peer_id, player_info)
|
_add_player(peer_id, player_info)
|
||||||
|
|
||||||
|
# Sync existing weapons to the newly joined player (but not to server itself)
|
||||||
|
if multiplayer.is_server() and peer_id != 1:
|
||||||
|
print("[Server] Syncing weapons to newly connected peer: ", peer_id)
|
||||||
|
print("[Server] Active weapons count: ", _active_weapons.size())
|
||||||
|
for weapon_id in _active_weapons.keys():
|
||||||
|
var weapon = _active_weapons[weapon_id]
|
||||||
|
if is_instance_valid(weapon) and weapon.weapon_data:
|
||||||
|
print("[Server] Sending weapon ", weapon_id, " to peer ", peer_id)
|
||||||
|
# Send current position and zero velocity for syncing
|
||||||
|
rpc_id(peer_id, "_client_spawn_weapon",
|
||||||
|
weapon.weapon_data.resource_path,
|
||||||
|
weapon.global_position,
|
||||||
|
Vector3.ZERO,
|
||||||
|
weapon_id
|
||||||
|
)
|
||||||
|
|
||||||
func _on_host_pressed():
|
func _on_host_pressed():
|
||||||
menu.hide()
|
menu.hide()
|
||||||
Network.start_host(nick_input.text.strip_edges(), skin_input.text.strip_edges().to_lower())
|
Network.start_host(nick_input.text.strip_edges(), skin_input.text.strip_edges().to_lower())
|
||||||
@@ -206,8 +223,10 @@ func _on_jemz_preset():
|
|||||||
|
|
||||||
# ---------- WEAPON SPAWNING ----------
|
# ---------- WEAPON SPAWNING ----------
|
||||||
## Spawn a weapon in the world (called from server, syncs to all clients)
|
## Spawn a weapon in the world (called from server, syncs to all clients)
|
||||||
@rpc("authority", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func spawn_world_weapon(weapon_data_path: String, spawn_position: Vector3, initial_velocity: Vector3, weapon_id: int):
|
func spawn_world_weapon(weapon_data_path: String, spawn_position: Vector3, initial_velocity: Vector3, weapon_id: int):
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] spawn_world_weapon called for weapon_id: ", weapon_id)
|
||||||
|
|
||||||
if not weapons_container:
|
if not weapons_container:
|
||||||
push_error("WeaponsContainer not found in level!")
|
push_error("WeaponsContainer not found in level!")
|
||||||
return
|
return
|
||||||
@@ -221,15 +240,81 @@ func spawn_world_weapon(weapon_data_path: String, spawn_position: Vector3, initi
|
|||||||
# Create WorldWeapon instance
|
# Create WorldWeapon instance
|
||||||
var world_weapon = WorldWeapon.new()
|
var world_weapon = WorldWeapon.new()
|
||||||
world_weapon.weapon_data = weapon_data
|
world_weapon.weapon_data = weapon_data
|
||||||
|
print("[DEBUG] About to set weapon_id. Parameter weapon_id = ", weapon_id)
|
||||||
|
world_weapon.weapon_id = weapon_id # Store the ID
|
||||||
|
print("[DEBUG] After setting weapon_id. world_weapon.weapon_id = ", world_weapon.weapon_id)
|
||||||
world_weapon.name = "WorldWeapon_" + str(weapon_id) # Deterministic name
|
world_weapon.name = "WorldWeapon_" + str(weapon_id) # Deterministic name
|
||||||
|
print("[DEBUG] Set weapon name to: ", world_weapon.name, " using weapon_id: ", weapon_id)
|
||||||
world_weapon.position = spawn_position
|
world_weapon.position = spawn_position
|
||||||
|
|
||||||
# Add to weapons container
|
# Remove existing weapon with same name if it exists (prevents duplicates)
|
||||||
weapons_container.add_child(world_weapon, true)
|
var weapon_path = NodePath(world_weapon.name)
|
||||||
|
if weapons_container.has_node(weapon_path):
|
||||||
|
print("[DEBUG] Weapon ", world_weapon.name, " already exists, removing old one first")
|
||||||
|
var old_weapon = weapons_container.get_node(weapon_path)
|
||||||
|
weapons_container.remove_child(old_weapon)
|
||||||
|
old_weapon.queue_free()
|
||||||
|
|
||||||
print("Spawned weapon: ", world_weapon.name, " at ", spawn_position)
|
# Add to weapons container
|
||||||
|
weapons_container.add_child(world_weapon)
|
||||||
|
print("[DEBUG] After add_child, weapon name in tree: ", world_weapon.name, " weapon_id: ", world_weapon.weapon_id)
|
||||||
|
|
||||||
|
# Track this weapon on the server (AFTER adding to tree so signals work)
|
||||||
|
if multiplayer.is_server():
|
||||||
|
_active_weapons[weapon_id] = world_weapon
|
||||||
|
print("[Server] Added weapon ", weapon_id, " to _active_weapons. Total: ", _active_weapons.size())
|
||||||
|
# Connect to the weapon's removal signal
|
||||||
|
world_weapon.tree_exiting.connect(_on_weapon_removed.bind(weapon_id))
|
||||||
|
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Spawned weapon: ", world_weapon.name, " at ", spawn_position)
|
||||||
|
|
||||||
# Apply velocity after physics is ready
|
# Apply velocity after physics is ready
|
||||||
if initial_velocity != Vector3.ZERO:
|
if initial_velocity != Vector3.ZERO:
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
world_weapon.linear_velocity = initial_velocity
|
world_weapon.linear_velocity = initial_velocity
|
||||||
|
|
||||||
|
## Remove a world weapon from all clients (called by WorldWeapon when picked up)
|
||||||
|
func remove_world_weapon(weapon_id: int):
|
||||||
|
print("[Server] remove_world_weapon called for weapon_id: ", weapon_id)
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
print("[ERROR] remove_world_weapon called on client!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Broadcast removal to all clients
|
||||||
|
print("[Server] Broadcasting removal RPC to all clients")
|
||||||
|
rpc("_remove_weapon_on_clients", weapon_id)
|
||||||
|
|
||||||
|
## RPC to remove weapon on all clients
|
||||||
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
|
func _remove_weapon_on_clients(weapon_id: int):
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] _remove_weapon_on_clients called for weapon_id: ", weapon_id)
|
||||||
|
var weapon_name = "WorldWeapon_" + str(weapon_id)
|
||||||
|
if weapons_container and weapons_container.has_node(weapon_name):
|
||||||
|
var weapon = weapons_container.get_node(weapon_name)
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Removing weapon ", weapon_name)
|
||||||
|
weapon.queue_free()
|
||||||
|
else:
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] WARNING: Weapon ", weapon_name, " not found in WeaponsContainer")
|
||||||
|
|
||||||
|
## Called when a weapon is removed (picked up or destroyed)
|
||||||
|
func _on_weapon_removed(weapon_id: int):
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Remove from tracking
|
||||||
|
if _active_weapons.has(weapon_id):
|
||||||
|
_active_weapons.erase(weapon_id)
|
||||||
|
print("Removed weapon ", weapon_id, " from active tracking")
|
||||||
|
|
||||||
|
## Client-only spawn (called via rpc_id for late-join sync)
|
||||||
|
@rpc("any_peer", "reliable")
|
||||||
|
func _client_spawn_weapon(weapon_data_path: String, spawn_position: Vector3, initial_velocity: Vector3, weapon_id: int):
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] _client_spawn_weapon received for weapon_id: ", weapon_id)
|
||||||
|
# This only runs on clients, not server (no call_local)
|
||||||
|
if multiplayer.is_server():
|
||||||
|
print("[ERROR] _client_spawn_weapon called on server!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Call the regular spawn function to create the weapon
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] Calling spawn_world_weapon locally")
|
||||||
|
spawn_world_weapon(weapon_data_path, spawn_position, initial_velocity, weapon_id)
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
extends Node3D
|
||||||
|
class_name LilguyBody
|
||||||
|
|
||||||
|
const LERP_VELOCITY: float = 0.15
|
||||||
|
|
||||||
|
@export_category("Objects")
|
||||||
|
@export var _character: CharacterBody3D = null
|
||||||
|
@export var animation_player: AnimationPlayer = null
|
||||||
|
|
||||||
|
func apply_rotation(_velocity: Vector3) -> void:
|
||||||
|
var new_rotation_y = lerp_angle(rotation.y, atan2(_velocity.x, _velocity.z), LERP_VELOCITY)
|
||||||
|
rotation.y = new_rotation_y
|
||||||
|
|
||||||
|
func animate(_velocity: Vector3) -> void:
|
||||||
|
if not animation_player:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Don't override attack animation if it's playing
|
||||||
|
if animation_player.is_playing() and animation_player.current_animation == "Attack_OneHand":
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if we're dashing
|
||||||
|
if _character._is_dashing:
|
||||||
|
if animation_player.current_animation != "Jump":
|
||||||
|
_play_animation("Jump")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not _character.is_on_floor():
|
||||||
|
if _velocity.y < 0:
|
||||||
|
# Falling - use FallIdle animation
|
||||||
|
_play_animation("FallIdle")
|
||||||
|
else:
|
||||||
|
_play_animation("Jump")
|
||||||
|
return
|
||||||
|
|
||||||
|
if _velocity:
|
||||||
|
if _character.is_running() and _character.is_on_floor():
|
||||||
|
# Sprint animation = Run for Lilguy
|
||||||
|
_play_animation("Run")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Walk animation for normal movement
|
||||||
|
_play_animation("Walk")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Idle - use FallIdle or Idle if it exists
|
||||||
|
if animation_player.has_animation("Idle"):
|
||||||
|
_play_animation("Idle")
|
||||||
|
else:
|
||||||
|
_play_animation("FallIdle")
|
||||||
|
|
||||||
|
func _play_animation(anim_name: String):
|
||||||
|
if animation_player and animation_player.has_animation(anim_name):
|
||||||
|
animation_player.play(anim_name)
|
||||||
|
# Silently ignore if animation doesn't exist
|
||||||
|
|
||||||
|
func play_attack() -> void:
|
||||||
|
if animation_player:
|
||||||
|
# Play attack animation once (don't loop)
|
||||||
|
animation_player.play("Attack_OneHand", -1, 1.0)
|
||||||
|
# Ensure it doesn't loop
|
||||||
|
animation_player.animation_set_next("Attack_OneHand", "")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cf7jky1bcs560
|
||||||
+76
-12
@@ -24,10 +24,10 @@ enum SkinColor { BLUE, YELLOW, GREEN, RED }
|
|||||||
@export var green_texture : CompressedTexture2D
|
@export var green_texture : CompressedTexture2D
|
||||||
@export var red_texture : CompressedTexture2D
|
@export var red_texture : CompressedTexture2D
|
||||||
|
|
||||||
@onready var _bottom_mesh: MeshInstance3D = get_node("3DGodotRobot/RobotArmature/Skeleton3D/Bottom")
|
@onready var _bottom_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Bottom")
|
||||||
@onready var _chest_mesh: MeshInstance3D = get_node("3DGodotRobot/RobotArmature/Skeleton3D/Chest")
|
@onready var _chest_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Chest")
|
||||||
@onready var _face_mesh: MeshInstance3D = get_node("3DGodotRobot/RobotArmature/Skeleton3D/Face")
|
@onready var _face_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Face")
|
||||||
@onready var _limbs_head_mesh: MeshInstance3D = get_node("3DGodotRobot/RobotArmature/Skeleton3D/Llimbs and head")
|
@onready var _limbs_head_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Llimbs and head")
|
||||||
|
|
||||||
var _current_speed: float
|
var _current_speed: float
|
||||||
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||||
@@ -61,6 +61,65 @@ func _ready():
|
|||||||
super._ready()
|
super._ready()
|
||||||
set_respawn_point(Vector3(0, 5, 0))
|
set_respawn_point(Vector3(0, 5, 0))
|
||||||
|
|
||||||
|
# Auto-find body node (needed for instanced scenes where @export NodePath doesn't work reliably)
|
||||||
|
if _body == null:
|
||||||
|
for child in get_children():
|
||||||
|
if child.name == "Armature" or child.name == "3DGodotRobot":
|
||||||
|
_body = child
|
||||||
|
print("Auto-found _body: ", child.name)
|
||||||
|
break
|
||||||
|
if _body == null:
|
||||||
|
push_error("Could not find body node (Armature or 3DGodotRobot)!")
|
||||||
|
|
||||||
|
# Auto-find spring arm offset
|
||||||
|
if _spring_arm_offset == null:
|
||||||
|
if has_node("SpringArmOffset"):
|
||||||
|
_spring_arm_offset = get_node("SpringArmOffset")
|
||||||
|
print("Auto-found _spring_arm_offset")
|
||||||
|
else:
|
||||||
|
push_error("Could not find SpringArmOffset!")
|
||||||
|
|
||||||
|
# Auto-find weapon attachments if not set
|
||||||
|
if _weapon_attachment == null:
|
||||||
|
if has_node("Armature/Skeleton3D/WeaponPoint"):
|
||||||
|
_weapon_attachment = get_node("Armature/Skeleton3D/WeaponPoint")
|
||||||
|
print("Auto-found _weapon_attachment")
|
||||||
|
elif has_node("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D"):
|
||||||
|
_weapon_attachment = get_node("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D")
|
||||||
|
print("Auto-found _weapon_attachment (robot)")
|
||||||
|
else:
|
||||||
|
print("WARNING: WeaponPoint not found! Check if you've added BoneAttachment3D nodes.")
|
||||||
|
if has_node("Armature/Skeleton3D"):
|
||||||
|
print("Skeleton3D children:")
|
||||||
|
var skeleton = get_node("Armature/Skeleton3D")
|
||||||
|
for child in skeleton.get_children():
|
||||||
|
print(" - ", child.name, " (", child.get_class(), ")")
|
||||||
|
|
||||||
|
# Auto-find weapon container
|
||||||
|
if _weapon_container == null and _weapon_attachment:
|
||||||
|
var container = _weapon_attachment.get_node_or_null("WeaponContainer")
|
||||||
|
if container:
|
||||||
|
_weapon_container = container
|
||||||
|
print("Auto-found _weapon_container")
|
||||||
|
|
||||||
|
# Auto-find offhand attachment
|
||||||
|
if _offhand_attachment == null:
|
||||||
|
if has_node("Armature/Skeleton3D/OffhandPoint"):
|
||||||
|
_offhand_attachment = get_node("Armature/Skeleton3D/OffhandPoint")
|
||||||
|
print("Auto-found _offhand_attachment")
|
||||||
|
elif has_node("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint"):
|
||||||
|
_offhand_attachment = get_node("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint")
|
||||||
|
print("Auto-found _offhand_attachment (robot)")
|
||||||
|
else:
|
||||||
|
print("WARNING: OffhandPoint not found!")
|
||||||
|
|
||||||
|
# Auto-find offhand container
|
||||||
|
if _offhand_container == null and _offhand_attachment:
|
||||||
|
var container = _offhand_attachment.get_node_or_null("OffhandContainer")
|
||||||
|
if container:
|
||||||
|
_offhand_container = container
|
||||||
|
print("Auto-found _offhand_container")
|
||||||
|
|
||||||
# Try to get optional health label
|
# Try to get optional health label
|
||||||
if has_node("PlayerNick/HealthLabel"):
|
if has_node("PlayerNick/HealthLabel"):
|
||||||
health_label = get_node("PlayerNick/HealthLabel")
|
health_label = get_node("PlayerNick/HealthLabel")
|
||||||
@@ -539,16 +598,19 @@ func _flash_hurt():
|
|||||||
if not _body:
|
if not _body:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Store original modulate
|
# Only works if _body has a modulate property (CanvasItem or some Node3D with visual children)
|
||||||
var original_modulate = _body.modulate
|
if "modulate" in _body:
|
||||||
|
# Store original modulate
|
||||||
|
var original_modulate = _body.modulate
|
||||||
|
|
||||||
# Flash red
|
# Flash red
|
||||||
_body.modulate = Color(1.5, 0.5, 0.5, 1.0)
|
_body.modulate = Color(1.5, 0.5, 0.5, 1.0)
|
||||||
|
|
||||||
# Return to normal after a brief moment
|
# Return to normal after a brief moment
|
||||||
await get_tree().create_timer(0.15).timeout
|
await get_tree().create_timer(0.15).timeout
|
||||||
if _body:
|
if _body:
|
||||||
_body.modulate = original_modulate
|
_body.modulate = original_modulate
|
||||||
|
# If no modulate, just skip the visual effect
|
||||||
|
|
||||||
## Weapon System
|
## Weapon System
|
||||||
func _setup_weapon_pickup_area():
|
func _setup_weapon_pickup_area():
|
||||||
@@ -589,9 +651,11 @@ func _on_weapon_area_exited(area: Area3D):
|
|||||||
## Equip a weapon from WorldWeapon data (receives resource path)
|
## Equip a weapon from WorldWeapon data (receives resource path)
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func equip_weapon_from_world(weapon_data_path: String):
|
func equip_weapon_from_world(weapon_data_path: String):
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] equip_weapon_from_world called for: ", weapon_data_path)
|
||||||
var data = load(weapon_data_path) as WeaponData
|
var data = load(weapon_data_path) as WeaponData
|
||||||
if data:
|
if data:
|
||||||
equip_weapon(data)
|
equip_weapon(data)
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] Equipped weapon: ", data.weapon_name)
|
||||||
else:
|
else:
|
||||||
push_error("Failed to load weapon data from: " + weapon_data_path)
|
push_error("Failed to load weapon data from: " + weapon_data_path)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class_name WorldWeapon
|
|||||||
|
|
||||||
@export var weapon_data: WeaponData
|
@export var weapon_data: WeaponData
|
||||||
|
|
||||||
|
var weapon_id: int = -1 # Set by Level when spawned
|
||||||
var _mesh_instance: Node3D = null
|
var _mesh_instance: Node3D = null
|
||||||
var _collision_shape: CollisionShape3D = null
|
var _collision_shape: CollisionShape3D = null
|
||||||
var _pickup_area: Area3D = null
|
var _pickup_area: Area3D = null
|
||||||
@@ -119,15 +120,30 @@ func try_pickup(player_id: int):
|
|||||||
push_error("WeaponData has no resource path!")
|
push_error("WeaponData has no resource path!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check weapon_id is valid
|
||||||
|
if weapon_id == -1:
|
||||||
|
push_error("WorldWeapon.try_pickup: weapon_id is -1! This weapon was not spawned correctly.")
|
||||||
|
print(" Weapon name: ", name)
|
||||||
|
print(" Weapon data: ", weapon_data.resource_path if weapon_data else "null")
|
||||||
|
return
|
||||||
|
|
||||||
# Tell the player to equip this weapon (on all clients)
|
# Tell the player to equip this weapon (on all clients)
|
||||||
|
print("[Server] Telling player ", player_id, " to equip weapon via RPC")
|
||||||
player.rpc("equip_weapon_from_world", resource_path)
|
player.rpc("equip_weapon_from_world", resource_path)
|
||||||
|
|
||||||
# Remove this world weapon from all clients
|
# Remove this world weapon from all clients using level's centralized system
|
||||||
rpc("_remove_from_all_clients")
|
print("[Server] Removing world weapon ", name, " (ID: ", weapon_id, ") via level.remove_world_weapon")
|
||||||
|
if level.has_method("remove_world_weapon"):
|
||||||
|
level.remove_world_weapon(weapon_id)
|
||||||
|
else:
|
||||||
|
push_error("Level doesn't have remove_world_weapon method!")
|
||||||
|
|
||||||
## Remove weapon from all clients
|
## Remove weapon from all clients
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func _remove_from_all_clients():
|
func _remove_from_all_clients():
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] Removing weapon ", name)
|
||||||
|
# Delay removal to ensure RPC is fully processed
|
||||||
|
await get_tree().process_frame
|
||||||
queue_free()
|
queue_free()
|
||||||
|
|
||||||
## Set weapon data and refresh
|
## Set weapon data and refresh
|
||||||
|
|||||||
Reference in New Issue
Block a user