Compare commits
6
Commits
Dashfix
...
2ec7d56511
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ec7d56511 | ||
|
|
b1f1016475 | ||
|
|
e192802f01 | ||
|
|
85e76d67ca | ||
|
|
6e4e3a3585 | ||
|
|
bdc507e852 |
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponData" load_steps=3 format=3 uid="uid://b2q62xc0jw4w3"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cehc5ckhq2byd" path="res://level/scenes/weapons/Applecoremesh.tscn" id="1_1ytxi"]
|
||||||
|
[ext_resource type="Script" uid="uid://d2homvlmrg6xs" path="res://level/scripts/weapon_data.gd" id="2_hfi3c"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_hfi3c")
|
||||||
|
weapon_name = "Apple Sword"
|
||||||
|
description = "yum"
|
||||||
|
damage = 20.0
|
||||||
|
attack_range = 3.5
|
||||||
|
attack_cooldown = 0.6
|
||||||
|
attack_animation = "Attack_TwoHandSwing"
|
||||||
|
knockback_force = 12.0
|
||||||
|
mesh_scene = ExtResource("1_1ytxi")
|
||||||
|
weight = 2.0
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
[gd_scene load_steps=6 format=3 uid="uid://db06e8q8f8bdq"]
|
[gd_scene load_steps=7 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="Script" uid="uid://c2si8gkbnde0c" path="res://level/scripts/player.gd" id="1_player"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b22ou40sbkavj" path="res://assets/characters/player/LilguyRigged.glb" id="2_lilguy"]
|
||||||
[ext_resource type="Script" uid="uid://cf7jky1bcs560" path="res://level/scripts/lilguy_body.gd" id="3_body"]
|
[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="4_spring"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
||||||
radius = 0.35796
|
radius = 0.35796
|
||||||
@@ -12,26 +13,35 @@ 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("AnimationPlayer:current_animation")
|
properties/1/path = NodePath("LilguyRigged/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("Armature:rotation")
|
properties/3/path = NodePath("LilguyRigged/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="Player" type="CharacterBody3D" node_paths=PackedStringArray("_body", "_spring_arm_offset", "_weapon_attachment", "_weapon_container", "_offhand_attachment", "_offhand_container")]
|
||||||
collision_mask = 3
|
collision_mask = 3
|
||||||
|
script = ExtResource("1_player")
|
||||||
|
_body = NodePath("LilguyRigged/Armature")
|
||||||
|
_spring_arm_offset = NodePath("SpringArmOffset")
|
||||||
|
_weapon_attachment = NodePath("LilguyRigged/Armature/Skeleton3D/WeaponPoint")
|
||||||
|
_weapon_container = NodePath("LilguyRigged/Armature/Skeleton3D/WeaponPoint/WeaponContainer")
|
||||||
|
_offhand_attachment = NodePath("LilguyRigged/Armature/Skeleton3D/OffhandPoint")
|
||||||
|
_offhand_container = NodePath("LilguyRigged/Armature/Skeleton3D/OffhandPoint/OffhandContainer")
|
||||||
|
|
||||||
[node name="Armature" parent="." index="0" node_paths=PackedStringArray("_character", "animation_player")]
|
[node name="LilguyRigged" parent="." instance=ExtResource("2_lilguy")]
|
||||||
|
|
||||||
|
[node name="Armature" parent="LilguyRigged" 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")
|
script = ExtResource("3_body")
|
||||||
_character = NodePath("..")
|
_character = NodePath("../..")
|
||||||
animation_player = NodePath("../AnimationPlayer")
|
animation_player = NodePath("../AnimationPlayer")
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Armature" index="0"]
|
[node name="Skeleton3D" parent="LilguyRigged/Armature" index="0"]
|
||||||
bones/0/position = Vector3(-0.32852697, 2.914154, -546.76843)
|
bones/0/position = Vector3(-0.32852697, 2.914154, -546.76843)
|
||||||
bones/0/rotation = Quaternion(-0.6608288, 0.28933647, -0.19178489, 0.6654384)
|
bones/0/rotation = Quaternion(-0.6608288, 0.28933647, -0.19178489, 0.6654384)
|
||||||
bones/1/position = Vector3(0.054167695, 63.219894, -3.33786e-06)
|
bones/1/position = Vector3(0.054167695, 63.219894, -3.33786e-06)
|
||||||
@@ -78,45 +88,47 @@ bones/23/position = Vector3(1.3624241e-05, 15.034077, 9.790485e-06)
|
|||||||
bones/23/rotation = Quaternion(0.11885707, 0.009521758, -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"]
|
[node name="WeaponPoint" type="BoneAttachment3D" parent="LilguyRigged/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)
|
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_name = "mixamorig_RightHand"
|
||||||
bone_idx = 14
|
bone_idx = 14
|
||||||
|
|
||||||
[node name="WeaponContainer" type="Node3D" parent="Armature/Skeleton3D/WeaponPoint" index="0"]
|
[node name="WeaponContainer" type="Node3D" parent="LilguyRigged/Armature/Skeleton3D/WeaponPoint"]
|
||||||
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)
|
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"]
|
[node name="OffhandPoint" type="BoneAttachment3D" parent="LilguyRigged/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)
|
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_name = "mixamorig_LeftHand"
|
||||||
bone_idx = 10
|
bone_idx = 10
|
||||||
|
|
||||||
[node name="OffhandContainer" type="Node3D" parent="Armature/Skeleton3D/OffhandPoint" index="0"]
|
[node name="OffhandContainer" type="Node3D" parent="LilguyRigged/Armature/Skeleton3D/OffhandPoint"]
|
||||||
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)
|
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="."]
|
||||||
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")
|
||||||
|
|
||||||
[node name="SpringArmOffset" type="Node3D" parent="." index="3" node_paths=PackedStringArray("_spring_arm")]
|
[node name="SpringArmOffset" type="Node3D" parent="." node_paths=PackedStringArray("_spring_arm")]
|
||||||
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
|
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
|
||||||
script = ExtResource("9_dlyie")
|
script = ExtResource("4_spring")
|
||||||
_spring_arm = NodePath("SpringArm3D")
|
_spring_arm = NodePath("SpringArm3D")
|
||||||
|
|
||||||
[node name="SpringArm3D" type="SpringArm3D" parent="SpringArmOffset" index="0"]
|
[node name="SpringArm3D" type="SpringArm3D" parent="SpringArmOffset"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
||||||
spring_length = 5.0
|
spring_length = 5.0
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="SpringArmOffset/SpringArm3D" index="0"]
|
[node name="Camera3D" type="Camera3D" parent="SpringArmOffset/SpringArm3D"]
|
||||||
current = true
|
current = true
|
||||||
|
|
||||||
[node name="PlayerNick" type="Node3D" parent="." index="4"]
|
[node name="PlayerNick" type="Node3D" parent="."]
|
||||||
|
|
||||||
[node name="Nickname" type="Label3D" parent="PlayerNick" index="0"]
|
[node name="Nickname" type="Label3D" parent="PlayerNick"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.97037, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.97037, 0)
|
||||||
billboard = 1
|
billboard = 1
|
||||||
outline_modulate = Color(0, 0, 0, 0.301961)
|
outline_modulate = Color(0, 0, 0, 0.301961)
|
||||||
text = "player name test"
|
text = "player name test"
|
||||||
|
|
||||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." index="5"]
|
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||||
replication_config = SubResource("SceneReplicationConfig_xbohm")
|
replication_config = SubResource("SceneReplicationConfig_xbohm")
|
||||||
|
|
||||||
|
[editable path="LilguyRigged"]
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
[gd_scene load_steps=19 format=3 uid="uid://dugaivbj1o66n"]
|
[gd_scene load_steps=20 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://wipqjhfqeuwd" 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://chkrcwlprbn88" 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"]
|
[ext_resource type="PackedScene" uid="uid://hd6pq287rgye" path="res://level/scenes/weapons/world_weapon_testsword.tscn" id="5_cwx4m"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://8c4l6s6x67vh" path="res://level/scenes/weapons/world_weapon_applecorer.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)
|
||||||
@@ -352,6 +353,9 @@ transform = Transform3D(15, 0, 0, 0, 15, 0, 0, 0, 15, 1.301034, -1.2294581, 2.06
|
|||||||
[node name="WorldWeaponSword" parent="WeaponsContainer" instance=ExtResource("5_cwx4m")]
|
[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)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.0268106, 2.6057472, 8.836907)
|
||||||
|
|
||||||
|
[node name="WorldWeaponSword2" parent="WeaponsContainer" instance=ExtResource("6_xerh7")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.32151043, 5.2709904)
|
||||||
|
|
||||||
[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"]
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cehc5ckhq2byd"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://c3e6e3s2q0uro" path="res://assets/Objects/Applecorer.glb" id="1_yadub"]
|
||||||
|
|
||||||
|
[node name="TestSwordMesh" type="Node3D"]
|
||||||
|
|
||||||
|
[node name="Applecorer" parent="." instance=ExtResource("1_yadub")]
|
||||||
|
transform = Transform3D(-0.3, 0, -2.6226834e-08, 0, 0.3, 0, 2.6226834e-08, 0, -0.3, 0, 0, 0)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://8c4l6s6x67vh"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ccnnd0y4jqiot" path="res://level/scripts/world_weapon.gd" id="1_7688s"]
|
||||||
|
[ext_resource type="Resource" uid="uid://b2q62xc0jw4w3" path="res://level/resources/weapon_applecorer.tres" id="2_7688s"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="1"]
|
||||||
|
size = Vector3(0.3, 0.3, 1.2)
|
||||||
|
|
||||||
|
[node name="WorldWeaponSword" type="RigidBody3D"]
|
||||||
|
collision_layer = 4
|
||||||
|
collision_mask = 2
|
||||||
|
mass = 2.0
|
||||||
|
script = ExtResource("1_7688s")
|
||||||
|
weapon_data = ExtResource("2_7688s")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("1")
|
||||||
@@ -48,9 +48,12 @@ func perform_attack() -> bool:
|
|||||||
if owner_character and owner_character.is_multiplayer_authority():
|
if owner_character and owner_character.is_multiplayer_authority():
|
||||||
owner_character._attack_timer = weapon_data.attack_cooldown
|
owner_character._attack_timer = weapon_data.attack_cooldown
|
||||||
|
|
||||||
# Play attack animation on owner
|
# Play attack animation on owner (use weapon's animation)
|
||||||
if owner_character._body:
|
if owner_character._body:
|
||||||
owner_character._body.play_attack()
|
var anim_name = weapon_data.attack_animation if weapon_data.attack_animation else "Attack_OneHand"
|
||||||
|
owner_character._body.play_attack(anim_name)
|
||||||
|
# Sync animation to other clients
|
||||||
|
owner_character._sync_attack_animation.rpc(anim_name)
|
||||||
|
|
||||||
# Delay damage until animation hits (roughly 70% through the animation)
|
# Delay damage until animation hits (roughly 70% through the animation)
|
||||||
# This makes the damage apply when the swing actually connects
|
# This makes the damage apply when the swing actually connects
|
||||||
|
|||||||
+133
-8
@@ -13,6 +13,8 @@ extends Node3D
|
|||||||
var _weapon_spawn_counter: int = 0
|
var _weapon_spawn_counter: int = 0
|
||||||
# Track active weapons for late-join sync (server-side only)
|
# Track active weapons for late-join sync (server-side only)
|
||||||
var _active_weapons: Dictionary = {} # weapon_id -> WorldWeapon reference
|
var _active_weapons: Dictionary = {} # weapon_id -> WorldWeapon reference
|
||||||
|
# Track if we've already initialized to prevent double-spawning
|
||||||
|
var _multiplayer_initialized: bool = false
|
||||||
|
|
||||||
# multiplayer chat
|
# multiplayer chat
|
||||||
@onready var message: LineEdit = $MultiplayerChat/VBoxContainer/HBoxContainer/Message
|
@onready var message: LineEdit = $MultiplayerChat/VBoxContainer/HBoxContainer/Message
|
||||||
@@ -23,6 +25,8 @@ var _active_weapons: Dictionary = {} # weapon_id -> WorldWeapon reference
|
|||||||
var chat_visible = false
|
var chat_visible = false
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
print("[Level] _ready() called. Peer ID: ", multiplayer.get_unique_id(), " Is server: ", multiplayer.is_server())
|
||||||
|
|
||||||
multiplayer_chat.hide()
|
multiplayer_chat.hide()
|
||||||
menu.show()
|
menu.show()
|
||||||
multiplayer_chat.set_process_input(true)
|
multiplayer_chat.set_process_input(true)
|
||||||
@@ -39,11 +43,28 @@ 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)
|
# Don't initialize weapons in _ready() - wait for Host/Join to be pressed
|
||||||
if not multiplayer.is_server():
|
# This is handled in initialize_multiplayer() which is called after the
|
||||||
|
# multiplayer peer is properly set up
|
||||||
|
print("[Level] _ready() complete - waiting for Host/Join")
|
||||||
|
|
||||||
|
func _on_connected_to_server():
|
||||||
|
print("[Level] Connected to server! Cleaning up manual weapons")
|
||||||
_cleanup_manual_weapons_on_client()
|
_cleanup_manual_weapons_on_client()
|
||||||
|
|
||||||
|
## Called by Network when multiplayer peer is set up
|
||||||
|
func initialize_multiplayer():
|
||||||
|
print("[Level] initialize_multiplayer called. is_server: ", multiplayer.is_server())
|
||||||
|
|
||||||
|
# Prevent double initialization
|
||||||
|
if _multiplayer_initialized:
|
||||||
|
print("[Level] Already initialized, skipping")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_multiplayer_initialized = true
|
||||||
|
|
||||||
|
if multiplayer.is_server():
|
||||||
|
print("[Level] Running server initialization")
|
||||||
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)
|
||||||
|
|
||||||
@@ -53,19 +74,40 @@ func _ready():
|
|||||||
# Spawn initial weapons when server starts
|
# Spawn initial weapons when server starts
|
||||||
_spawn_initial_weapons()
|
_spawn_initial_weapons()
|
||||||
|
|
||||||
|
# Spawn the host player (peer ID 1)
|
||||||
|
print("[Level] Spawning host player")
|
||||||
|
var host_info = Network.players.get(1, {"nick": "Host", "skin": "blue"})
|
||||||
|
_add_player(1, host_info)
|
||||||
|
else:
|
||||||
|
# Client initialization - clean up manual weapons immediately
|
||||||
|
print("[Level] Running client initialization - cleaning up manual weapons")
|
||||||
|
_cleanup_manual_weapons_on_client()
|
||||||
|
|
||||||
func _cleanup_manual_weapons_on_client():
|
func _cleanup_manual_weapons_on_client():
|
||||||
"""Remove manually placed weapons on clients (server will sync them via RPC)"""
|
"""Remove manually placed weapons on clients (server will sync them via RPC)"""
|
||||||
|
print("[Client ", multiplayer.get_unique_id(), "] _cleanup_manual_weapons_on_client called")
|
||||||
|
|
||||||
if not weapons_container:
|
if not weapons_container:
|
||||||
|
print("[Client] No weapons_container found!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print("[Client] WeaponsContainer has ", weapons_container.get_child_count(), " children")
|
||||||
|
|
||||||
var weapons_to_remove = []
|
var weapons_to_remove = []
|
||||||
for child in weapons_container.get_children():
|
for child in weapons_container.get_children():
|
||||||
if child is WorldWeapon and child.weapon_id == -1:
|
print("[Client] Checking child: ", child.name, " (type: ", child.get_class(), ")")
|
||||||
|
if child is WorldWeapon:
|
||||||
|
print("[Client] - Is WorldWeapon with weapon_id: ", child.weapon_id)
|
||||||
|
if child.weapon_id == -1:
|
||||||
weapons_to_remove.append(child)
|
weapons_to_remove.append(child)
|
||||||
|
print("[Client] - Marked for removal")
|
||||||
|
|
||||||
|
print("[Client] Found ", weapons_to_remove.size(), " weapons to remove")
|
||||||
for weapon in weapons_to_remove:
|
for weapon in weapons_to_remove:
|
||||||
print("[Client] Removing manually placed weapon: ", weapon.name)
|
print("[Client] Removing manually placed weapon: ", weapon.name)
|
||||||
weapon.queue_free()
|
# Use immediate removal to prevent RPC errors
|
||||||
|
weapons_container.remove_child(weapon)
|
||||||
|
weapon.free()
|
||||||
|
|
||||||
func _initialize_manual_weapons():
|
func _initialize_manual_weapons():
|
||||||
"""Initialize any WorldWeapon nodes manually placed in the level scene"""
|
"""Initialize any WorldWeapon nodes manually placed in the level scene"""
|
||||||
@@ -144,16 +186,29 @@ func _spawn_initial_weapons():
|
|||||||
)
|
)
|
||||||
|
|
||||||
func _on_player_connected(peer_id, player_info):
|
func _on_player_connected(peer_id, player_info):
|
||||||
|
print("[Server] _on_player_connected called for peer ", peer_id, " with info: ", player_info)
|
||||||
_add_player(peer_id, player_info)
|
_add_player(peer_id, player_info)
|
||||||
|
|
||||||
|
# Sync existing players to the newly joined player
|
||||||
|
if multiplayer.is_server() and peer_id != 1:
|
||||||
|
print("[Server] Syncing existing players to newly connected peer: ", peer_id)
|
||||||
|
# Wait a frame to ensure new player is fully initialized
|
||||||
|
await get_tree().process_frame
|
||||||
|
for existing_player in players_container.get_children():
|
||||||
|
var existing_id = int(existing_player.name)
|
||||||
|
if existing_id != peer_id: # Don't sync the player to themselves
|
||||||
|
print("[Server] Syncing existing player ", existing_id, " to peer ", peer_id)
|
||||||
|
rpc_id(peer_id, "_spawn_player_local", existing_id, existing_player.position)
|
||||||
|
|
||||||
# Sync existing weapons to the newly joined player (but not to server itself)
|
# Sync existing weapons to the newly joined player (but not to server itself)
|
||||||
if multiplayer.is_server() and peer_id != 1:
|
if multiplayer.is_server() and peer_id != 1:
|
||||||
print("[Server] Syncing weapons to newly connected peer: ", peer_id)
|
print("[Server] Syncing weapons to newly connected peer: ", peer_id)
|
||||||
|
print("[Server] Active weapons in _active_weapons: ", _active_weapons.keys())
|
||||||
print("[Server] Active weapons count: ", _active_weapons.size())
|
print("[Server] Active weapons count: ", _active_weapons.size())
|
||||||
for weapon_id in _active_weapons.keys():
|
for weapon_id in _active_weapons.keys():
|
||||||
var weapon = _active_weapons[weapon_id]
|
var weapon = _active_weapons[weapon_id]
|
||||||
if is_instance_valid(weapon) and weapon.weapon_data:
|
if is_instance_valid(weapon) and weapon.weapon_data:
|
||||||
print("[Server] Sending weapon ", weapon_id, " to peer ", peer_id)
|
print("[Server] Sending weapon ", weapon_id, " (", weapon.weapon_data.weapon_name, ") at position ", weapon.global_position, " to peer ", peer_id)
|
||||||
# Send current position and zero velocity for syncing
|
# Send current position and zero velocity for syncing
|
||||||
rpc_id(peer_id, "_client_spawn_weapon",
|
rpc_id(peer_id, "_client_spawn_weapon",
|
||||||
weapon.weapon_data.resource_path,
|
weapon.weapon_data.resource_path,
|
||||||
@@ -161,25 +216,87 @@ func _on_player_connected(peer_id, player_info):
|
|||||||
Vector3.ZERO,
|
Vector3.ZERO,
|
||||||
weapon_id
|
weapon_id
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
print("[Server] Skipping invalid weapon ", weapon_id)
|
||||||
|
|
||||||
|
# Sync equipped weapons for all existing players to the newly joined player
|
||||||
|
print("[Server] Syncing equipped weapons to newly connected peer: ", peer_id)
|
||||||
|
for player_node in players_container.get_children():
|
||||||
|
var player = player_node as Character
|
||||||
|
if player and is_instance_valid(player):
|
||||||
|
# Skip the newly joined player (they don't have weapons yet)
|
||||||
|
if int(player.name) == peer_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sync main hand weapon
|
||||||
|
if player.equipped_weapon and player.equipped_weapon.weapon_data:
|
||||||
|
print("[Server] Syncing main hand weapon for player ", player.name, " to peer ", peer_id)
|
||||||
|
player.rpc_id(peer_id, "equip_weapon_from_world",
|
||||||
|
player.equipped_weapon.weapon_data.resource_path)
|
||||||
|
|
||||||
|
# Sync off-hand weapon
|
||||||
|
if player.equipped_offhand and player.equipped_offhand.weapon_data:
|
||||||
|
print("[Server] Syncing off-hand weapon for player ", player.name, " to peer ", peer_id)
|
||||||
|
player.rpc_id(peer_id, "equip_weapon_from_world",
|
||||||
|
player.equipped_offhand.weapon_data.resource_path)
|
||||||
|
|
||||||
func _on_host_pressed():
|
func _on_host_pressed():
|
||||||
|
print("[Level] Host button pressed")
|
||||||
menu.hide()
|
menu.hide()
|
||||||
|
print("[Level] Calling Network.start_host()")
|
||||||
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())
|
||||||
|
print("[Level] Waiting one frame...")
|
||||||
|
await get_tree().process_frame
|
||||||
|
print("[Level] Calling initialize_multiplayer()")
|
||||||
|
initialize_multiplayer()
|
||||||
|
|
||||||
func _on_join_pressed():
|
func _on_join_pressed():
|
||||||
|
print("[Level] Join button pressed")
|
||||||
menu.hide()
|
menu.hide()
|
||||||
|
print("[Level] Calling Network.join_game()")
|
||||||
Network.join_game(nick_input.text.strip_edges(), skin_input.text.strip_edges().to_lower(), address_input.text.strip_edges())
|
Network.join_game(nick_input.text.strip_edges(), skin_input.text.strip_edges().to_lower(), address_input.text.strip_edges())
|
||||||
|
print("[Level] Waiting one frame...")
|
||||||
|
await get_tree().process_frame
|
||||||
|
print("[Level] Calling initialize_multiplayer()")
|
||||||
|
initialize_multiplayer()
|
||||||
|
|
||||||
func _add_player(id: int, player_info : Dictionary):
|
func _add_player(id: int, player_info : Dictionary):
|
||||||
|
print("[Level] _add_player called for peer ", id, " with info: ", player_info)
|
||||||
|
|
||||||
|
# Server spawns player and replicates to all clients via RPC
|
||||||
|
if multiplayer.is_server():
|
||||||
if players_container.has_node(str(id)):
|
if players_container.has_node(str(id)):
|
||||||
|
print("[Level] Player ", id, " already exists, skipping")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var spawn_pos = get_spawn_point()
|
||||||
|
print("[Level] Server spawning player ", id, " at ", spawn_pos)
|
||||||
|
|
||||||
|
# Spawn on server and all clients (call_local does both)
|
||||||
|
rpc("_spawn_player_local", id, spawn_pos)
|
||||||
|
|
||||||
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
|
func _spawn_player_local(id: int, spawn_pos: Vector3):
|
||||||
|
if players_container.has_node(str(id)):
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Player ", id, " already exists, skipping")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Creating player instance for peer ", id)
|
||||||
var player = player_scene.instantiate()
|
var player = player_scene.instantiate()
|
||||||
player.name = str(id)
|
player.name = str(id)
|
||||||
player.position = get_spawn_point()
|
player.position = spawn_pos
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Adding player to PlayersContainer")
|
||||||
players_container.add_child(player, true)
|
players_container.add_child(player, true)
|
||||||
|
print("[Peer ", multiplayer.get_unique_id(), "] Player ", id, " spawned at ", player.position)
|
||||||
|
|
||||||
var nick = Network.players[id]["nick"]
|
# Get player info from Network
|
||||||
player.nickname.text = nick
|
var player_info = Network.players.get(id, {"nick": "Player", "skin": "blue"})
|
||||||
|
|
||||||
|
var nick = player_info["nick"]
|
||||||
|
# Access nickname directly via node path since @onready hasn't loaded yet
|
||||||
|
var nickname_label = player.get_node_or_null("PlayerNick/Nickname")
|
||||||
|
if nickname_label:
|
||||||
|
nickname_label.text = nick
|
||||||
# player.rpc("change_nick", nick)
|
# player.rpc("change_nick", nick)
|
||||||
|
|
||||||
# Set up HUD for local player
|
# Set up HUD for local player
|
||||||
@@ -356,6 +473,14 @@ func remove_world_weapon(weapon_id: int):
|
|||||||
print("[ERROR] remove_world_weapon called on client!")
|
print("[ERROR] remove_world_weapon called on client!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Immediately remove from active weapons to prevent late-join sync issues
|
||||||
|
if _active_weapons.has(weapon_id):
|
||||||
|
_active_weapons.erase(weapon_id)
|
||||||
|
print("[Server] Removed weapon ", weapon_id, " from _active_weapons. Remaining: ", _active_weapons.size())
|
||||||
|
print("[Server] Remaining weapon IDs: ", _active_weapons.keys())
|
||||||
|
else:
|
||||||
|
print("[Server] WARNING: Weapon ", weapon_id, " not found in _active_weapons!")
|
||||||
|
|
||||||
# Broadcast removal to all clients
|
# Broadcast removal to all clients
|
||||||
print("[Server] Broadcasting removal RPC to all clients")
|
print("[Server] Broadcasting removal RPC to all clients")
|
||||||
rpc("_remove_weapon_on_clients", weapon_id)
|
rpc("_remove_weapon_on_clients", weapon_id)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func animate(_velocity: Vector3) -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Don't override attack animation if it's playing
|
# Don't override attack animation if it's playing
|
||||||
if animation_player.is_playing() and animation_player.current_animation == "Attack_OneHand":
|
if animation_player.is_playing() and animation_player.current_animation.begins_with("Attack"):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if we're dashing
|
# Check if we're dashing
|
||||||
@@ -54,9 +54,14 @@ func _play_animation(anim_name: String):
|
|||||||
animation_player.play(anim_name)
|
animation_player.play(anim_name)
|
||||||
# Silently ignore if animation doesn't exist
|
# Silently ignore if animation doesn't exist
|
||||||
|
|
||||||
func play_attack() -> void:
|
func play_attack(anim_name: String = "Attack_OneHand") -> void:
|
||||||
if animation_player:
|
if animation_player:
|
||||||
# Play attack animation once (don't loop)
|
# Play attack animation once (don't loop)
|
||||||
|
if animation_player.has_animation(anim_name):
|
||||||
|
animation_player.play(anim_name, -1, 1.0)
|
||||||
|
animation_player.animation_set_next(anim_name, "")
|
||||||
|
else:
|
||||||
|
# Fallback to default if animation doesn't exist
|
||||||
|
push_warning("Animation '%s' not found, using Attack_OneHand" % anim_name)
|
||||||
animation_player.play("Attack_OneHand", -1, 1.0)
|
animation_player.play("Attack_OneHand", -1, 1.0)
|
||||||
# Ensure it doesn't loop
|
|
||||||
animation_player.animation_set_next("Attack_OneHand", "")
|
animation_player.animation_set_next("Attack_OneHand", "")
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ var player_info = {
|
|||||||
signal player_connected(peer_id, player_info)
|
signal player_connected(peer_id, player_info)
|
||||||
signal server_disconnected
|
signal server_disconnected
|
||||||
|
|
||||||
func _process(_delta):
|
|
||||||
if Input.is_action_just_pressed("quit"):
|
|
||||||
get_tree().quit(0)
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
multiplayer.server_disconnected.connect(_on_connection_failed)
|
multiplayer.server_disconnected.connect(_on_connection_failed)
|
||||||
multiplayer.connection_failed.connect(_on_server_disconnected)
|
multiplayer.connection_failed.connect(_on_server_disconnected)
|
||||||
|
|||||||
+10
-2
@@ -392,7 +392,7 @@ func _perform_attack():
|
|||||||
|
|
||||||
# Fallback to default unarmed attack
|
# Fallback to default unarmed attack
|
||||||
# Don't attack if already attacking
|
# Don't attack if already attacking
|
||||||
if _body and _body.animation_player and _body.animation_player.current_animation == "Attack1":
|
if _body and _body.animation_player and _body.animation_player.current_animation.begins_with("Attack"):
|
||||||
return
|
return
|
||||||
|
|
||||||
if _attack_timer > 0:
|
if _attack_timer > 0:
|
||||||
@@ -402,7 +402,9 @@ func _perform_attack():
|
|||||||
|
|
||||||
# Play attack animation once
|
# Play attack animation once
|
||||||
if _body:
|
if _body:
|
||||||
_body.play_attack()
|
_body.play_attack("Attack_OneHand")
|
||||||
|
# Sync animation to other clients
|
||||||
|
_sync_attack_animation.rpc("Attack_OneHand")
|
||||||
|
|
||||||
# Find nearest enemy in range
|
# Find nearest enemy in range
|
||||||
var space_state = get_world_3d().direct_space_state
|
var space_state = get_world_3d().direct_space_state
|
||||||
@@ -524,6 +526,12 @@ func _perform_dash():
|
|||||||
|
|
||||||
# Animation is handled by the Body's animate function (Jump animation plays during dash)
|
# Animation is handled by the Body's animate function (Jump animation plays during dash)
|
||||||
|
|
||||||
|
## Sync attack animation to all clients
|
||||||
|
@rpc("any_peer", "call_remote", "unreliable")
|
||||||
|
func _sync_attack_animation(anim_name: String):
|
||||||
|
if _body:
|
||||||
|
_body.play_attack(anim_name)
|
||||||
|
|
||||||
## Override hurt animation from BaseUnit
|
## Override hurt animation from BaseUnit
|
||||||
func _play_hurt_animation():
|
func _play_hurt_animation():
|
||||||
if _body and _body.animation_player:
|
if _body and _body.animation_player:
|
||||||
|
|||||||
Reference in New Issue
Block a user