diff --git a/Player.tscn b/Player.tscn index e71e115..6d70160 100644 --- a/Player.tscn +++ b/Player.tscn @@ -5,72 +5,32 @@ [ext_resource type="Script" uid="uid://b720l6m0ehrj3" path="res://camera_follow.gd" id="3_kne1u"] [ext_resource type="PackedScene" uid="uid://qy6qrxdfgx2g" path="res://Assets/bull_v10.fbx" id="4_e80uo"] -[sub_resource type="SphereShape3D" id="SphereShape3D_cxr5p"] - [sub_resource type="SphereShape3D" id="SphereShape3D_kne1u"] radius = 0.3 -[sub_resource type="SphereShape3D" id="SphereShape3D_e80uo"] -radius = 0.75698936 +[sub_resource type="SphereShape3D" id="SphereShape3D_chest"] +radius = 0.35 -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cxr5p"] -radius = 0.00026695352 -height = 0.002669535 +[sub_resource type="SphereShape3D" id="SphereShape3D_belly"] +radius = 0.40 -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_tdg3f"] -radius = 0.0002866844 -height = 0.002866844 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_dtqjt"] -radius = 0.00028599252 -height = 0.0028599252 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_5ncpa"] -radius = 0.0003274492 -height = 0.003274492 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_wi0c6"] -radius = 0.00028593137 -height = 0.0028593137 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_hp6x6"] -radius = 0.03 -height = 0.15 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3tkgg"] -radius = 0.02 -height = 0.1 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_l6ohk"] -radius = 0.0003321008 -height = 0.003321008 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oudfo"] -radius = 0.00026695352 -height = 0.002669535 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_d6rpx"] -radius = 0.0002866844 -height = 0.002866844 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_r3kul"] -radius = 0.00028599252 -height = 0.0028599252 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_jatw7"] -radius = 0.0003274492 -height = 0.003274492 - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_rv870"] -radius = 0.00028593137 -height = 0.0028593137 +[sub_resource type="SphereShape3D" id="SphereShape3D_hips"] +radius = 0.30 [node name="Player" type="CharacterBody3D" unique_id=165510044] script = ExtResource("1_xhfnw") -[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=314502219] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9729152, 0) -shape = SubResource("SphereShape3D_cxr5p") +[node name="CollisionChest" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.2) +shape = SubResource("SphereShape3D_chest") + +[node name="CollisionBelly" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.1) +shape = SubResource("SphereShape3D_belly") + +[node name="CollisionHips" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.4) +shape = SubResource("SphereShape3D_hips") [node name="SpringArmPivot" type="Node3D" parent="." unique_id=1671817680] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.22, 0) @@ -92,282 +52,4 @@ lerp_power = 6.0 [node name="bull_v10" parent="." unique_id=1386495166 instance=ExtResource("4_e80uo")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.655, 0.23) -[node name="PhysicalBoneSimulator3D" type="PhysicalBoneSimulator3D" parent="bull_v10/Armature/Skeleton3D" parent_id_path=PackedInt32Array(1386495166, 1366639819) index="2" unique_id=1777458180] - -[node name="Physical Bone COG" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1541330657] -transform = Transform3D(-0.007564789, 0.00409347, -0.0051007345, -0.0065401834, -0.004734768, 0.0058998317, -7.7972817e-10, 0.007799071, 0.006258956, 0.0017633128, -0.0020413985, 0.0078065563) -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000120311975, -0.0005881116, 0.34540433) -body_offset = Transform3D(-0.0075647887, 0.00409347, -0.005100734, -7.752023e-14, 0.007799073, 0.0062589566, 0.006540184, 0.004734768, -0.005899834, 0.0017633127, -0.002157284, 0.0020413995) -bone_name = "COG" - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone COG" unique_id=1877016782] -transform = Transform3D(0.99999994, 9.834766e-07, 7.1525574e-07, -8.046627e-07, 0, 1.0000002, 1.013279e-06, -1, -1.1920929e-07, 0.0016898364, 0.10711694, 0.35998654) -shape = SubResource("SphereShape3D_e80uo") - -[node name="Physical Bone front_leg_1_L" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1675398557] -transform = Transform3D(-0.009573112, -0.0002095241, -0.0028829917, -0.00019426788, -0.009904512, 0.0013648955, -0.0028840597, 0.001362637, 0.009477629, 0.00391145, -0.004264981, 0.004374394) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00015264872, -7.158299e-05, 0.13297947) -body_offset = Transform3D(0.009999981, 1.0186341e-10, -3.5106495e-10, 3.965397e-10, 8.1490725e-10, -0.010000003, -2.1827873e-10, 0.009999998, 8.1490725e-10, -1.5264377e-06, 0.001329795, 7.157214e-07) -bone_name = "front_leg_1_L" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone front_leg_1_L" unique_id=1745285030] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_cxr5p") - -[node name="Physical Bone front_leg_2_L" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=71283091] -transform = Transform3D(-0.009977277, 3.905031e-05, -0.00067263393, -3.9138667e-05, -0.009999925, -4.19114e-09, -0.00067262875, 2.6284176e-06, 0.009977355, 0.0043927236, -0.0044475948, 0.0016755704) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.6548756e-05, -4.3348646e-05, 0.14317834) -body_offset = Transform3D(0.009999981, -2.4007022e-08, 8.510059e-09, 1.0807027e-10, 8.691763e-10, -0.009999999, 2.3192115e-08, 0.009999998, -3.1224912e-10, -1.6670674e-07, 0.0014317832, 4.3353066e-07) -bone_name = "front_leg_2_L" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone front_leg_2_L" unique_id=102232034] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_tdg3f") - -[node name="Physical Bone rear_leg_1_L" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=641436769] -transform = Transform3D(-0.008503459, -0.005138319, -0.0011352889, 0.0042592627, -0.007987581, 0.0042493828, -0.0030902892, 0.0031298958, 0.008980752, 0.0047028894, 0.0036388426, 0.0065502548) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.000323987, 0.0006103788, 0.14196822) -body_offset = Transform3D(0.010000004, -4.656613e-10, -2.3283064e-10, -5.820766e-10, -1.1641532e-09, -0.010000005, 4.656613e-10, 0.009999999, -9.313226e-10, -3.2398384e-06, 0.0014196831, -6.1036553e-06) -bone_name = "rear_leg_1_L" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_1_L" unique_id=892834462] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_dtqjt") - -[node name="Physical Bone rear_leg_2_L" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1185899204] -transform = Transform3D(-0.00915834, 0.0029074468, 0.002769761, -0.0039623342, -0.0054236636, -0.0074083614, -0.00065171614, -0.007882301, 0.006119203, 0.004407039, 0.0042441166, 0.0042632855) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00036044084, 0.0010113111, 0.16385125) -body_offset = Transform3D(0.0100000035, 1.1571683e-07, -5.5879354e-08, -5.3318217e-08, 2.561137e-08, -0.010000003, -1.1525117e-07, 0.010000005, 2.6542693e-08, -3.5953708e-06, 0.0016385128, -1.0117423e-05) -bone_name = "rear_leg_2_L" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_2_L" unique_id=1284740745] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_5ncpa") - -[node name="Physical Bone rear_leg_3_L" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=35949848] -transform = Transform3D(-0.009840026, -0.00014057018, -0.0017759949, -0.00018075595, -0.009838606, 0.0017802179, -0.001772356, 0.0017838409, 0.009678672, 0.0042075394, 0.00519469, 0.0018702713) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.2520294e-06, 2.4616318e-07, 0.14296634) -body_offset = Transform3D(0.010000004, -1.1350494e-09, -8.5128704e-10, -1.4551915e-10, 2.3283064e-10, -0.010000004, 1.4551915e-11, 0.010000002, -2.561137e-09, -6.239861e-08, 0.001429664, -2.0954758e-09) -bone_name = "rear_leg_3_L" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_3_L" unique_id=1811789092] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_wi0c6") - -[node name="Physical Bone tail_1" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=409059252] -transform = Transform3D(0.010000001, 0, -2.7184845e-34, -2.7184847e-34, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.009381076, 0.0109815765) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.8774088e-23, -6.9849165e-07, 0.08855743) -body_offset = Transform3D(0.010000001, 0, -2.7184843e-34, -2.7184854e-34, -6.661338e-16, -0.0100000035, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.0008855746, 6.9849193e-09) -bone_name = "tail_1" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_1" unique_id=1083045052] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_2" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1184926284] -transform = Transform3D(0.010000001, 0, -2.662741e-34, -2.6627415e-34, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.011170757, 0.0109815765) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.8774088e-23, -6.9849165e-07, 0.09041118) -body_offset = Transform3D(0.010000001, 0, -2.6627408e-34, -2.662742e-34, -6.661338e-16, -0.0100000035, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.0009041121, 6.9849193e-09) -bone_name = "tail_2" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_2" unique_id=790298871] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_3" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1650092450] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.013070463, 0.010998246) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.877409e-23, -0.0016675792, 0.09955974) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000004, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.0009955978, 1.6675796e-05) -bone_name = "tail_3" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_3" unique_id=2053684626] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_4" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1695718220] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.015077802, 0.010981572) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.877409e-23, -9.313222e-08, 0.10117438) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000004, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.0010117441, 9.313226e-10) -bone_name = "tail_4" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_4" unique_id=1860507411] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_5" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=839180583] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.016923692, 0.0109815765) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.877409e-23, -6.984917e-07, 0.083414994) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000004, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.00083415024, 6.9849193e-09) -bone_name = "tail_5" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_5" unique_id=389279420] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_6" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1413495806] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.018541092, 0.0109815765) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.877409e-23, -6.984917e-07, 0.07832466) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000004, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.00078324694, 6.9849193e-09) -bone_name = "tail_6" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_6" unique_id=1892370858] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_hp6x6") - -[node name="Physical Bone tail_tip_1" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1460110003] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.019823557, 0.0109815765) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.877409e-23, -6.984917e-07, 0.05696246) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000004, 0, 0.0100000035, -6.661338e-16, 3.877409e-25, 0.0005696248, 6.9849193e-09) -bone_name = "tail_tip_1" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_tip_1" unique_id=2108943626] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_3tkgg") - -[node name="Physical Bone tail_tip_2" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=2147217275] -transform = Transform3D(0.010000001, 0, 0, 0, -5.551115e-16, -0.010000002, 0, 0.010000002, -5.551115e-16, 2.2598148e-19, 0.020996556, 0.010997413) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.8774088e-23, -0.001584179, 0.064297915) -body_offset = Transform3D(0.010000001, 0, 0, 0, -6.661338e-16, -0.010000006, 0, 0.010000004, -6.661338e-16, 3.877409e-25, 0.0006429795, 1.5841797e-05) -bone_name = "tail_tip_2" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone tail_tip_2" unique_id=1720964713] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_3tkgg") - -[node name="Physical Bone head" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=599730911] -transform = Transform3D(-0.010000001, -1.5099572e-09, 3.9762948e-16, 3.7627612e-17, 2.3841862e-09, 0.010000002, -1.5099573e-09, 0.010000002, -2.3841862e-09, -3.7788734e-13, -0.009213124, 0.010918824) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.2797929e-10, -0.0017600593, 0.16605066) -body_offset = Transform3D(0.010000001, -8.881785e-16, -4.2358053e-17, 2.6490955e-19, 2.220446e-16, -0.0100000035, 3.3306685e-16, 0.0100000035, 2.220446e-16, -2.2797875e-12, 0.0016605072, 1.76006e-05) -bone_name = "head" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone head" unique_id=505205665] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_l6ohk") - -[node name="Physical Bone front_leg_1_R" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=866459371] -transform = Transform3D(-0.009573112, 0.0002095241, 0.0028829917, 0.00019426788, -0.009904512, 0.0013648955, 0.0028840597, 0.001362637, 0.009477629, -0.00391145, -0.004264981, 0.004374394) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00015264872, -7.158299e-05, 0.13297947) -body_offset = Transform3D(0.009999981, -1.0186341e-10, 3.5106495e-10, -3.965397e-10, 8.1490725e-10, -0.010000003, 2.1827873e-10, 0.009999998, 8.1490725e-10, 1.5264377e-06, 0.001329795, 7.157214e-07) -bone_name = "front_leg_1_R" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone front_leg_1_R" unique_id=937418203] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_oudfo") - -[node name="Physical Bone front_leg_2_R" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1283418469] -transform = Transform3D(-0.009977277, -3.905031e-05, 0.00067263393, 3.9138667e-05, -0.009999925, -4.19114e-09, 0.00067262875, 2.6284176e-06, 0.009977355, -0.0043927236, -0.0044475948, 0.0016755704) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.6548756e-05, -4.3348646e-05, 0.14317834) -body_offset = Transform3D(0.009999981, 2.4007022e-08, -8.510059e-09, -1.0807027e-10, 8.691763e-10, -0.009999999, -2.3192115e-08, 0.009999998, -3.1224912e-10, 1.6670674e-07, 0.0014317832, 4.3353066e-07) -bone_name = "front_leg_2_R" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone front_leg_2_R" unique_id=258563666] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_d6rpx") - -[node name="Physical Bone rear_leg_1_R" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=216944416] -transform = Transform3D(-0.008503459, 0.005138319, 0.0011352889, -0.0042592627, -0.007987581, 0.0042493828, 0.0030902892, 0.0031298958, 0.008980752, -0.0047028894, 0.0036388426, 0.0065502548) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000323987, 0.0006103788, 0.14196822) -body_offset = Transform3D(0.010000004, 4.656613e-10, 2.3283064e-10, 5.820766e-10, -1.1641532e-09, -0.010000005, -4.656613e-10, 0.009999999, -9.313226e-10, 3.2398384e-06, 0.0014196831, -6.1036553e-06) -bone_name = "rear_leg_1_R" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_1_R" unique_id=314882241] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_r3kul") - -[node name="Physical Bone rear_leg_2_R" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=1097552018] -transform = Transform3D(-0.00915834, -0.0029074468, -0.002769761, 0.0039623342, -0.0054236636, -0.0074083614, 0.00065171614, -0.007882301, 0.006119203, -0.004407039, 0.0042441166, 0.0042632855) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00036044084, 0.0010113111, 0.16385125) -body_offset = Transform3D(0.0100000035, -1.1571683e-07, 5.5879354e-08, 5.3318217e-08, 2.561137e-08, -0.010000003, 1.1525117e-07, 0.010000005, 2.6542693e-08, 3.5953708e-06, 0.0016385128, -1.0117423e-05) -bone_name = "rear_leg_2_R" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_2_R" unique_id=473621577] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_jatw7") - -[node name="Physical Bone rear_leg_3_R" type="PhysicalBone3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D" unique_id=2066167688] -transform = Transform3D(-0.009840026, 0.00014057018, 0.0017759949, 0.00018075595, -0.009838606, 0.0017802179, 0.001772356, 0.0017838409, 0.009678672, -0.0042075394, 0.00519469, 0.0018702713) -joint_type = 1 -joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.2520294e-06, 2.4616318e-07, 0.14296634) -body_offset = Transform3D(0.010000004, 1.1350494e-09, 8.5128704e-10, 1.4551915e-10, 2.3283064e-10, -0.010000004, -1.4551915e-11, 0.010000002, -2.561137e-09, 6.239861e-08, 0.001429664, -2.0954758e-09) -bone_name = "rear_leg_3_R" -joint_constraints/bias = 0.3 -joint_constraints/damping = 1.0 -joint_constraints/impulse_clamp = 0.0 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="bull_v10/Armature/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone rear_leg_3_R" unique_id=2020682212] -transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0) -shape = SubResource("CapsuleShape3D_rv870") - [editable path="bull_v10"] diff --git a/bull_legs.gd b/bull_legs.gd new file mode 100644 index 0000000..af2a4b4 --- /dev/null +++ b/bull_legs.gd @@ -0,0 +1,437 @@ +extends Node +## Procedural leg stepping, body wobble, and tail simulation. +## Created at runtime by player.gd; call setup() before the first _process tick. + +# ── Leg State Machine ───────────────────────────────────────────────────────── +enum LegState { GALLOP, LAUNCH, RAGDOLL, TUCK, LAND } + +# Leg IK config — rest offsets are in bull_v10 local space +const LEG_CFG: Dictionary = { + "FL": { "root": &"front_leg_1_L", "tip": &"front_leg_2_L_end", + "rest": Vector3(-0.30, -0.25, -0.50) }, + "FR": { "root": &"front_leg_1_R", "tip": &"front_leg_2_R_end", + "rest": Vector3( 0.30, -0.25, -0.50) }, + "BL": { "root": &"rear_leg_1_L", "tip": &"rear_leg_3_L_end", + "rest": Vector3(-0.30, -0.25, 0.60) }, + "BR": { "root": &"rear_leg_1_R", "tip": &"rear_leg_3_R_end", + "rest": Vector3( 0.30, -0.25, 0.60) }, +} + +# Gallop gait phase offset per leg (0.0–1.0 cycle). +const LEG_PHASES: Dictionary = { "BL": 0.0, "FL": 0.2, "BR": 0.5, "FR": 0.7 } + +# Per-leg ragdoll phase offsets for variety +const RAGDOLL_PHASE: Dictionary = { "FL": 0.0, "FR": 1.3, "BL": 2.6, "BR": 3.9 } + +var _player: CharacterBody3D +var _bull: Node3D +var _skeleton: Skeleton3D + +# Per-leg state +var _ik: Dictionary = {} # id -> SkeletonIK3D +var _foot: Dictionary = {} # id -> current foot (world) +var _step_from: Dictionary = {} # id -> Vector3 +var _step_to: Dictionary = {} # id -> Vector3 +var _step_t: Dictionary = {} # id -> float (1.0 = planted) + +# Body wobble +var _prev_vel: Vector3 = Vector3.ZERO +var _tilt_x: float = 0.0 +var _tilt_z: float = 0.0 +var _aerial_pitch: float = 0.0 + +# Leg state machine +var _leg_state: LegState = LegState.GALLOP +var _state_time: float = 0.0 # time spent in current state +var _was_on_floor: bool = true +var _launch_feet: Dictionary = {} # frozen foot positions at launch + +# Gait +var _gait_phase: float = 0.0 + +# Tail — Verlet chain simulation +const TAIL_BONES: Array = [ + &"tail_1", &"tail_2", &"tail_3", &"tail_4", + &"tail_5", &"tail_6", + &"tail_tip_1", &"tail_tip_2", &"tail_tip_2_end", +] +var _tail_idx: Array[int] = [] +var _tail_world: Array[Vector3] = [] +var _tail_prev: Array[Vector3] = [] +var _tail_lengths: Array[float] = [] +var _tail_rest_dir: Array[Vector3] = [] + + +func setup(player: CharacterBody3D, bull: Node3D) -> void: + _player = player + _bull = bull + _skeleton = _find_skeleton(bull) + if not _skeleton: + push_error("BullLegs: Skeleton3D not found under bull_v10") + return + + # Disable any physical bone simulator — we don't need it + var phys_sim := _find_physical_bones(_skeleton) + if phys_sim: + phys_sim.active = false + + _setup_tail() + + for id: String in LEG_CFG: + var cfg: Dictionary = LEG_CFG[id] + + var ik := SkeletonIK3D.new() + ik.root_bone = cfg["root"] + ik.tip_bone = cfg["tip"] + ik.interpolation = 1.0 + ik.min_distance = 0.01 + _skeleton.add_child(ik) + ik.start() + _ik[id] = ik + + var world_rest: Vector3 = _bull.to_global(cfg["rest"]) + _foot[id] = world_rest + _step_from[id] = world_rest + _step_to[id] = world_rest + _step_t[id] = 1.0 + + +func _setup_tail() -> void: + for bone_name: StringName in TAIL_BONES: + var idx: int = _skeleton.find_bone(bone_name) + if idx == -1: + push_warning("BullLegs: tail bone '%s' not found" % bone_name) + continue + _tail_idx.append(idx) + + if _tail_idx.size() < 2: + push_error("BullLegs: not enough tail bones found") + return + + for i in range(_tail_idx.size() - 1): + var a: Transform3D = _skeleton.get_bone_global_rest(_tail_idx[i]) + var b: Transform3D = _skeleton.get_bone_global_rest(_tail_idx[i + 1]) + _tail_lengths.append((b.origin - a.origin).length()) + _tail_rest_dir.append((b.origin - a.origin).normalized()) + _tail_rest_dir.append(_tail_rest_dir[-1]) + + for idx: int in _tail_idx: + var world: Vector3 = _skeleton.to_global(_skeleton.get_bone_global_rest(idx).origin) + _tail_world.append(world) + _tail_prev.append(world) + + +func _process(delta: float) -> void: + if not _skeleton: + return + _wobble_body(delta) + _update_leg_state(delta) + _step_feet(delta) + _apply_ik() + _update_tail(delta) + _apply_tail() + + +# ── Body Wobble ─────────────────────────────────────────────────────────────── + +func _wobble_body(delta: float) -> void: + var vel: Vector3 = _player.velocity + var accel: Vector3 = (vel - _prev_vel) / maxf(delta, 0.001) + _prev_vel = vel + + var la: Vector3 = _bull.global_transform.basis.inverse() * accel + + var tx := clampf(-la.z * DP.f("body_tilt_fwd"), -0.35, 0.35) + var tz := clampf(-la.x * DP.f("body_tilt_side"), -0.25, 0.25) + + _tilt_x = lerpf(_tilt_x, tx, delta * 10.0) + _tilt_z = lerpf(_tilt_z, tz, delta * 10.0) + + var flat_spd: float = Vector2(vel.x, vel.z).length() + var target_pitch: float = 0.0 + if not _player.is_on_floor() and flat_spd > 0.5: + target_pitch = clampf(-atan2(vel.y, flat_spd) * DP.f("aerial_pitch_scale"), -1.4, 1.4) + _aerial_pitch = lerpf(_aerial_pitch, target_pitch, delta * DP.f("aerial_pitch_speed")) + + _bull.rotation.x = _tilt_x + _aerial_pitch + _bull.rotation.z = _tilt_z + + +# ── Leg State Machine ───────────────────────────────────────────────────────── + +func _update_leg_state(delta: float) -> void: + var on_floor: bool = _player.is_on_floor() + _state_time += delta + + match _leg_state: + LegState.GALLOP: + if not on_floor and _was_on_floor: + # Just left the ground — enter LAUNCH + _leg_state = LegState.LAUNCH + _state_time = 0.0 + # Freeze current foot positions + for id: String in _foot: + _launch_feet[id] = _foot[id] + + LegState.LAUNCH: + if on_floor: + _leg_state = LegState.GALLOP + _state_time = 0.0 + elif _state_time > DP.f("launch_hold_time"): + _leg_state = LegState.RAGDOLL + _state_time = 0.0 + + LegState.RAGDOLL: + if on_floor: + _leg_state = LegState.GALLOP + _state_time = 0.0 + elif _player.velocity.y < DP.f("tuck_apex_threshold"): + # Approaching or past apex — start tucking + _leg_state = LegState.TUCK + _state_time = 0.0 + + LegState.TUCK: + if on_floor: + _leg_state = LegState.GALLOP + _state_time = 0.0 + else: + # Check for ground proximity — transition to LAND + var space := _player.get_world_3d().direct_space_state + var start := _player.global_position + var end_pt := start + Vector3.DOWN * DP.f("jump_land_threshold") + var params := PhysicsRayQueryParameters3D.create(start, end_pt) + params.exclude = [_player.get_rid()] + var hit := space.intersect_ray(params) + if hit: + _leg_state = LegState.LAND + _state_time = 0.0 + + LegState.LAND: + if on_floor: + _leg_state = LegState.GALLOP + _state_time = 0.0 + + _was_on_floor = on_floor + + +# ── Feet Stepping ───────────────────────────────────────────────────────────── + +func _step_feet(delta: float) -> void: + match _leg_state: + LegState.GALLOP: + _step_gallop(delta) + LegState.LAUNCH: + _step_launch(delta) + LegState.RAGDOLL: + _step_ragdoll(delta) + LegState.TUCK: + _step_tuck(delta) + LegState.LAND: + _step_land(delta) + + +func _step_gallop(delta: float) -> void: + var threshold: float = DP.f("leg_step_threshold") + var duration: float = DP.f("leg_step_duration") + var height: float = DP.f("leg_step_height") + var overshoot: float = DP.f("leg_step_overshoot") + var flat_spd: float = Vector2(_player.velocity.x, _player.velocity.z).length() + + var prev_phase: float = _gait_phase + _gait_phase = fmod(_gait_phase + flat_spd * DP.f("gait_freq") * delta, 1.0) + + # Advance in-progress steps + for id: String in _step_t: + if _step_t[id] >= 1.0: + continue + _step_t[id] = minf(_step_t[id] + delta / duration, 1.0) + var t: float = _step_t[id] + var e: float = t * t * (3.0 - 2.0 * t) + var arc: float = height * sin(t * PI) + _foot[id] = _step_from[id].lerp(_step_to[id], e) + Vector3(0.0, arc, 0.0) + + # Trigger steps when gait phase crosses each leg's offset + for id: String in LEG_PHASES: + if _step_t[id] < 1.0: + continue + if not _phase_crossed(prev_phase, _gait_phase, LEG_PHASES[id]): + continue + var ideal: Vector3 = _ideal_foot(id) + if _foot[id].distance_to(ideal) < threshold: + continue + _step_from[id] = _foot[id] + _step_to[id] = ideal + _player.velocity * duration * overshoot + _step_t[id] = 0.0 + + +func _step_launch(_delta: float) -> void: + # Hold feet at their frozen launch positions + for id: String in _foot: + if _launch_feet.has(id): + _foot[id] = _launch_feet[id] + + +func _step_ragdoll(delta: float) -> void: + # Procedural floppy dangle — NOT real physics ragdoll + var time: float = _state_time + var amp: float = DP.f("ragdoll_wobble_amp") + # Amplitude grows over time (legs spread more the longer airborne) + var grow: float = clampf(time * 0.8, 0.0, 1.5) + + for id: String in _foot: + var rest: Vector3 = LEG_CFG[id]["rest"] + var phase: float = RAGDOLL_PHASE[id] + var dangle_offset := Vector3( + sin(time * 3.0 + phase) * amp * grow, # side sway + -0.4 - sin(time * 2.0 + phase) * 0.1 * grow, # gravity pull down + cos(time * 2.5 + phase) * amp * grow # forward/back wobble + ) + _foot[id] = _bull.to_global(rest + dangle_offset) + + +func _step_tuck(delta: float) -> void: + # Smoothly pull legs into tucked position under the body + var speed: float = DP.f("jump_tuck_speed") + for id: String in _foot: + var tucked: Vector3 = _get_tucked_foot(id) + _foot[id] = _foot[id].lerp(tucked, clampf(delta * speed, 0.0, 1.0)) + + +func _step_land(delta: float) -> void: + # Extend legs from tucked pose toward ideal ground positions, resume gallop cycle + var speed: float = DP.f("jump_tuck_speed") * 1.5 # slightly faster extend + for id: String in _foot: + var ideal: Vector3 = _ideal_foot(id) + _foot[id] = _foot[id].lerp(ideal, clampf(delta * speed, 0.0, 1.0)) + + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +func _phase_crossed(prev: float, curr: float, target: float) -> bool: + if curr >= prev: + return target >= prev and target < curr + else: + return target >= prev or target < curr + + +func _ideal_foot(id: String) -> Vector3: + var world_rest: Vector3 = _bull.to_global(LEG_CFG[id]["rest"]) + + # Raycast for ground + var space := _player.get_world_3d().direct_space_state + var params := PhysicsRayQueryParameters3D.create( + world_rest + Vector3(0.0, 0.6, 0.0), + world_rest + Vector3(0.0, -1.4, 0.0), + ) + params.exclude = [_player.get_rid()] + var hit := space.intersect_ray(params) + return hit["position"] if hit else world_rest + + +func _get_tucked_foot(id: String) -> Vector3: + var rest: Vector3 = LEG_CFG[id]["rest"] + var tucked_local := rest + Vector3(0.0, DP.f("jump_tuck_up"), DP.f("jump_tuck_fwd")) + return _bull.to_global(tucked_local) + + +func _apply_ik() -> void: + for id: String in _ik: + var ik: SkeletonIK3D = _ik[id] + var target_local: Vector3 = _skeleton.get_parent().to_local(_foot[id]) + ik.target = Transform3D(Basis.IDENTITY, target_local) + + +# ── Tail ────────────────────────────────────────────────────────────────────── + +func _update_tail(delta: float) -> void: + if _tail_world.is_empty(): + return + + var gravity := Vector3(0.0, -DP.f("tail_gravity"), 0.0) + var damping := 1.0 - clampf(DP.f("tail_damping"), 0.0, 0.99) + + # Re-anchor root joint + var anchor: Vector3 = _skeleton.to_global(_skeleton.get_bone_global_rest(_tail_idx[0]).origin) + _tail_world[0] = anchor + _tail_prev[0] = anchor + + # Idle wag force + var flat_speed: float = Vector2(_player.velocity.x, _player.velocity.z).length() + var wag_strength: float = lerpf(DP.f("tail_wag_idle"), 0.0, clampf(flat_speed / 3.0, 0.0, 1.0)) + var time: float = Time.get_ticks_msec() / 1000.0 + + # Verlet integrate + for i in range(1, _tail_world.size()): + var vel: Vector3 = (_tail_world[i] - _tail_prev[i]) * damping + _tail_prev[i] = _tail_world[i] + # Add wag force (sinusoidal side-to-side) + var wag := _bull.global_transform.basis.x * sin(time * DP.f("tail_wag_freq") + i * 0.5) * wag_strength + _tail_world[i] = _tail_world[i] + vel + gravity * (delta * delta) + wag * delta + + # Length constraint + for i in range(1, _tail_world.size()): + var dir: Vector3 = _tail_world[i] - _tail_world[i - 1] + var len: float = dir.length() + var target_len: float = _tail_lengths[i - 1] + if len > 0.0001: + _tail_world[i] = _tail_world[i - 1] + dir * (target_len / len) + else: + _tail_world[i] = _tail_world[i - 1] + Vector3(0.0, -target_len, 0.0) + + # Body collision pass — prevent tail from clipping through torso + # Use bull's local space; body is approximated as a capsule along Z axis + var body_radius: float = DP.f("tail_body_radius") + for i in range(1, _tail_world.size()): + var p := _bull.to_local(_tail_world[i]) + # Capsule centered at roughly (0, 0, 0.1) in bull local space, half-length 0.4 along Z + var closest := Vector3(0.0, 0.0, clampf(p.z, -0.3, 0.5)) + var diff := p - closest + var dist := diff.length() + if dist < body_radius and dist > 0.001: + p = closest + diff.normalized() * body_radius + _tail_world[i] = _bull.to_global(p) + + +func _apply_tail() -> void: + if _tail_world.is_empty(): + return + + for i in range(_tail_world.size()): + var idx: int = _tail_idx[i] + var pos_skel: Vector3 = _skeleton.to_local(_tail_world[i]) + + var point_dir_skel: Vector3 + if i < _tail_world.size() - 1: + point_dir_skel = (_skeleton.to_local(_tail_world[i + 1]) - pos_skel).normalized() + else: + var prev_skel: Vector3 = _skeleton.to_local(_tail_world[i - 1]) + point_dir_skel = (pos_skel - prev_skel).normalized() + + var rest_dir: Vector3 = _tail_rest_dir[i] + var rest_t: Transform3D = _skeleton.get_bone_global_rest(idx) + var new_basis: Basis + if rest_dir.length_squared() > 0.0001 and point_dir_skel.length_squared() > 0.0001: + new_basis = Basis(Quaternion(rest_dir, point_dir_skel)) * rest_t.basis + else: + new_basis = rest_t.basis + + _skeleton.set_bone_global_pose_override(idx, Transform3D(new_basis, pos_skel), 1.0, false) + + +# ── Utility ─────────────────────────────────────────────────────────────────── + +func _find_skeleton(node: Node) -> Skeleton3D: + if node is Skeleton3D: + return node as Skeleton3D + for child in node.get_children(): + var r := _find_skeleton(child) + if r: + return r + return null + + +func _find_physical_bones(node: Node) -> PhysicalBoneSimulator3D: + for child in node.get_children(): + if child is PhysicalBoneSimulator3D: + return child as PhysicalBoneSimulator3D + return null diff --git a/bull_legs.gd.uid b/bull_legs.gd.uid new file mode 100644 index 0000000..c0f8777 --- /dev/null +++ b/bull_legs.gd.uid @@ -0,0 +1 @@ +uid://bvtw8s41wrrx diff --git a/debug_menu.gd b/debug_menu.gd index 8bd31fe..b3053a3 100644 --- a/debug_menu.gd +++ b/debug_menu.gd @@ -110,7 +110,11 @@ func _build_params(content: VBoxContainer) -> void: content.add_child(header) for key: String in sections[section]: - _add_float_row(content, key, DP.get_all()[key]) + var meta: Dictionary = DP.get_all()[key] + if meta["type"] == TYPE_FLOAT: + _add_float_row(content, key, meta) + elif meta["type"] == TYPE_BOOL: + _add_bool_row(content, key, meta) func _add_float_row(parent: VBoxContainer, key: String, meta: Dictionary) -> void: @@ -161,6 +165,47 @@ func _add_float_row(parent: VBoxContainer, key: String, meta: Dictionary) -> voi ) +func _add_bool_row(parent: VBoxContainer, key: String, meta: Dictionary) -> void: + var row := HBoxContainer.new() + row.size_flags_horizontal = Control.SIZE_FILL + parent.add_child(row) + + var lbl := Label.new() + lbl.text = key.replace("_", " ") + lbl.custom_minimum_size.x = 140 + lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + row.add_child(lbl) + + var btn := Button.new() + btn.toggle_mode = true + btn.button_pressed = meta["value"] + btn.text = "HIDDEN" if not meta["value"] else "SHOWN" + _update_btn_style(btn, meta["value"]) + row.add_child(btn) + + btn.toggled.connect(func(v: bool) -> void: + btn.text = "HIDDEN" if not v else "SHOWN" + _update_btn_style(btn, v) + DP.set_value(key, v) + ) + + DP.any_changed.connect(func(changed_key: String, _val: Variant) -> void: + if changed_key != key and changed_key != "__all__": + return + var v_b: bool = DP.b(key) + btn.set_pressed_no_signal(v_b) + btn.text = "HIDDEN" if not v_b else "SHOWN" + _update_btn_style(btn, v_b) + ) + + +func _update_btn_style(btn: Button, active: bool) -> void: + if active: + btn.add_theme_color_override("font_color", Color(0.4, 1.0, 0.5)) + else: + btn.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) + + func _on_save() -> void: DP.save() _status_label.text = "Saved!" @@ -169,8 +214,21 @@ func _on_save() -> void: ) -func _on_dp_changed(_key: String, _value: Variant) -> void: - pass # individual rows handle their own updates via signal +func _on_dp_changed(key: String, _value: Variant) -> void: + if key == "show_collisions" or key == "__all__": + var active: bool = DP.b("show_collisions") + get_tree().debug_collisions_hint = active + # Force refresh of all collision shapes + _refresh_collisions(get_tree().root, active) + + +func _refresh_collisions(node: Node, active: bool) -> void: + if node is CollisionShape3D or node is CollisionPolygon3D: + node.visible = !node.visible # Trigger a notification + node.visible = !node.visible + + for child in node.get_children(): + _refresh_collisions(child, active) func _input(event: InputEvent) -> void: diff --git a/debug_menu.gd.uid b/debug_menu.gd.uid new file mode 100644 index 0000000..af3590f --- /dev/null +++ b/debug_menu.gd.uid @@ -0,0 +1 @@ +uid://via815ow6p5r diff --git a/debug_params.gd b/debug_params.gd index 0d9a61e..323fb2c 100644 --- a/debug_params.gd +++ b/debug_params.gd @@ -17,18 +17,21 @@ func _ready() -> void: func _register_all() -> void: # ── Movement ────────────────────────────────────────────────────────────── - _reg_f("Movement", "walk_speed", 6.0, 1.0, 40.0) - _reg_f("Movement", "charge_speed", 18.0, 5.0, 60.0) - _reg_f("Movement", "walk_accel", 14.0, 1.0, 80.0) - _reg_f("Movement", "charge_accel", 40.0, 5.0, 120.0) - _reg_f("Movement", "deceleration", 12.0, 1.0, 50.0) - _reg_f("Movement", "charge_decel", 3.5, 0.5, 20.0) - _reg_f("Movement", "jump_velocity", 4.5, 1.0, 20.0) - # ── Turning ─────────────────────────────────────────────────────────────── - _reg_f("Turning", "walk_turn_fast", 8.0, 0.5, 25.0) - _reg_f("Turning", "walk_turn_slow", 3.5, 0.5, 15.0) - _reg_f("Turning", "charge_turn", 0.55, 0.05, 3.0, 0.01) - _reg_f("Turning", "visual_turn_speed", 12.0, 1.0, 40.0) + _reg_f("Movement", "kick_impulse", 7.0, 0.5, 200.0) + _reg_f("Movement", "kick_interval", 0.22, 0.05, 2.0, 0.01) + _reg_f("Movement", "kick_vertical", 3.5, 0.0, 100.0) + _reg_f("Movement", "kick_spread", 12.0, 0.0, 90.0) + _reg_f("Movement", "kick_force_var", 0.20, 0.0, 2.0, 0.05) + _reg_f("Movement", "charge_kick_mult", 3.0, 1.0, 50.0) + _reg_f("Movement", "roll_damping", 0.30, 0.01, 0.99, 0.01) + _reg_f("Movement", "max_speed", 20.0, 2.0, 1000.0) + _reg_f("Movement", "jump_velocity", 4.5, 1.0, 200.0) + _reg_f("Movement", "visual_turn_speed", 12.0, 1.0, 200.0) + _reg_f("Movement", "bounce_restitution", 0.55, 0.0, 2.0, 0.05) + _reg_f("Movement", "bounce_min_impact", 1.0, 0.0, 20.0) + # ── Dust speed thresholds ───────────────────────────────────────────────── + _reg_f("Dust", "dust_walk_spd", 1.5, 0.1, 10.0) + _reg_f("Dust", "dust_charge_spd", 7.0, 1.0, 20.0) # ── Dust ────────────────────────────────────────────────────────────────── _reg_f("Dust", "dust_lifetime", 0.9, 0.1, 4.0) _reg_f("Dust", "dust_explosiveness", 0.35, 0.0, 1.0) @@ -42,6 +45,32 @@ func _register_all() -> void: _reg_f("Dust", "charge_scale_max", 2.2, 0.05, 6.0) _reg_f("Dust", "charge_vel_min", 2.8, 0.1, 20.0) _reg_f("Dust", "charge_vel_max", 5.5, 0.1, 20.0) + # ── Body wobble ─────────────────────────────────────────────────────────── + _reg_f("Body", "body_tilt_fwd", 0.035, 0.0, 0.15, 0.005) + _reg_f("Body", "body_tilt_side", 0.025, 0.0, 0.15, 0.005) + _reg_f("Body", "aerial_pitch_scale", 0.7, 0.0, 4.0, 0.05) + _reg_f("Body", "aerial_pitch_speed", 6.0, 0.5, 30.0) + # ── Legs ────────────────────────────────────────────────────────────────── + _reg_f("Legs", "gait_freq", 0.30, 0.0, 5.0, 0.01) + _reg_f("Legs", "leg_step_threshold", 0.12, 0.01, 1.5, 0.01) + _reg_f("Legs", "leg_step_height", 0.32, 0.0, 1.5, 0.01) + _reg_f("Legs", "leg_step_duration", 0.10, 0.02, 0.5, 0.01) + _reg_f("Legs", "leg_step_overshoot", 1.2, 0.0, 5.0, 0.1) + _reg_f("Legs", "jump_tuck_fwd", 0.2, -1.0, 1.0, 0.05) + _reg_f("Legs", "jump_tuck_up", 0.3, 0.0, 1.5, 0.05) + _reg_f("Legs", "jump_tuck_speed", 10.0, 1.0, 30.0) + _reg_f("Legs", "jump_land_threshold", 1.5, 0.0, 5.0, 0.1) + _reg_f("Legs", "launch_hold_time", 0.15, 0.0, 1.0, 0.01) + _reg_f("Legs", "ragdoll_wobble_amp", 0.15, 0.0, 1.0, 0.01) + _reg_f("Legs", "tuck_apex_threshold", 1.0, -5.0, 5.0, 0.1) + # ── Tail ────────────────────────────────────────────────────────────────── + _reg_f("Tail", "tail_gravity", 9.8, 0.0, 30.0) + _reg_f("Tail", "tail_damping", 0.08, 0.0, 0.5, 0.01) + _reg_f("Tail", "tail_body_radius", 0.35, 0.05, 2.0, 0.05) + _reg_f("Tail", "tail_wag_freq", 4.0, 0.5, 15.0) + _reg_f("Tail", "tail_wag_idle", 0.3, 0.0, 2.0, 0.05) + # ── Debug ───────────────────────────────────────────────────────────────── + _reg_b("Debug", "show_collisions", false) func _reg_f(section: String, key: String, default: float, @@ -57,11 +86,25 @@ func _reg_f(section: String, key: String, default: float, } +func _reg_b(section: String, key: String, default: bool) -> void: + _params[key] = { + "value": default, + "default": default, + "section": section, + "type": TYPE_BOOL, + } + + ## Read a float param. Panics on unknown key — intentional: typos should be loud. func f(key: String) -> float: return _params[key]["value"] as float +## Read a bool param. +func b(key: String) -> bool: + return _params[key]["value"] as bool + + func set_value(key: String, value: Variant) -> void: if not _params.has(key): return diff --git a/debug_params.gd.uid b/debug_params.gd.uid new file mode 100644 index 0000000..628db50 --- /dev/null +++ b/debug_params.gd.uid @@ -0,0 +1 @@ +uid://b3ix5yi7b3ynh diff --git a/player.gd b/player.gd index 74d1f28..d04f4af 100644 --- a/player.gd +++ b/player.gd @@ -1,6 +1,6 @@ extends CharacterBody3D -# Hoof positions in bull_v10 local space — adjust y/z if puffs don't land on the hooves +# Hoof positions in bull_v10 local space — dust emitter offsets const HOOF_OFFSETS: Array = [ Vector3(-0.30, -0.25, -0.50), Vector3( 0.30, -0.25, -0.50), @@ -11,10 +11,13 @@ const HOOF_OFFSETS: Array = [ @onready var camera_pivot: Node3D = $SpringArmPivot @onready var cube_guy: Node3D = $bull_v10 -var current_speed: float = 0.0 -var facing_angle: float = 0.0 - var _hoof_emitters: Array[CPUParticles3D] = [] +var _legs: Node +var _debug_shapes: Array[MeshInstance3D] = [] + +var _kick_timer: float = 0.0 +var _was_on_floor: bool = false +var _pre_slide_vel_y: float = 0.0 enum _DustState { NONE, WALK, CHARGE } var _dust_state: _DustState = _DustState.NONE @@ -22,7 +25,16 @@ var _dust_state: _DustState = _DustState.NONE func _ready() -> void: _setup_hoof_dust() + _setup_legs() DP.any_changed.connect(_on_dp_changed) + _sync_debug_vis() + + +func _setup_legs() -> void: + _legs = Node.new() + _legs.set_script(load("res://bull_legs.gd")) + add_child(_legs) + _legs.call(&"setup", self, cube_guy) func _setup_hoof_dust() -> void: @@ -97,8 +109,10 @@ func _set_dust_state(new_state: _DustState) -> void: p.emitting = true -func _on_dp_changed(_key: String, _val: Variant) -> void: - # Force re-apply so slider changes take effect without needing a state transition +func _on_dp_changed(key: String, _val: Variant) -> void: + if key == "show_collisions" or key == "__all__": + _sync_debug_vis() + if _hoof_emitters.is_empty() or _dust_state == _DustState.NONE: return var prev := _dust_state @@ -106,13 +120,79 @@ func _on_dp_changed(_key: String, _val: Variant) -> void: _set_dust_state(prev) +func _sync_debug_vis() -> void: + _clear_debug_vis() + if not DP.b("show_collisions"): + return + + # Material for wireframes + var mat := StandardMaterial3D.new() + mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + mat.no_depth_test = true + mat.albedo_color = Color(0, 1, 0, 0.5) + mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + + # Find all collision shapes in self and children + _create_debug_meshes(self, mat) + + +func _create_debug_meshes(node: Node, mat: Material) -> void: + if node is CollisionShape3D and node.shape: + var mi := MeshInstance3D.new() + mi.mesh = node.shape.get_debug_mesh() + mi.material_override = mat + # We add it as child of the collision shape to follow its transform + node.add_child(mi) + _debug_shapes.append(mi) + + for child in node.get_children(): + _create_debug_meshes(child, mat) + + +func _clear_debug_vis() -> void: + for mi in _debug_shapes: + if is_instance_valid(mi): + mi.get_parent().remove_child(mi) + mi.queue_free() + _debug_shapes.clear() + + +func _apply_kick(direction: Vector3, charging: bool) -> void: + var base := DP.f("kick_impulse") * (DP.f("charge_kick_mult") if charging else 1.0) + + # Random force variance — each kick punches a different amount + var force := base * randf_range(1.0 - DP.f("kick_force_var"), 1.0 + DP.f("kick_force_var")) + + # Random angle spread — slight mis-kick like a real ball + var spread := deg_to_rad(randf_range(-DP.f("kick_spread"), DP.f("kick_spread"))) + var kick_dir := direction.rotated(Vector3.UP, spread) + + velocity.x += kick_dir.x * force + velocity.z += kick_dir.z * force + + # Vertical arc — only when grounded so arcs don't chain infinitely in the air + if is_on_floor(): + velocity.y += DP.f("kick_vertical") * randf_range(0.75, 1.25) + + # Cap immediately after kick so charge doesn't overshoot by too much + var flat := Vector2(velocity.x, velocity.z) + var spd := flat.length() + var cap := DP.f("max_speed") * (DP.f("charge_kick_mult") if charging else 1.0) + if spd > cap: + var s := cap / spd + velocity.x *= s + velocity.z *= s + + func _physics_process(delta: float) -> void: + # ── Gravity & jump ──────────────────────────────────────────────────────── if not is_on_floor(): velocity += get_gravity() * delta - if Input.is_action_just_pressed("jump") and is_on_floor(): + if Input.is_action_just_pressed(&"jump") and is_on_floor(): velocity.y = DP.f("jump_velocity") + # ── Camera-relative input direction ─────────────────────────────────────── var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back") var cam_basis := camera_pivot.global_transform.basis var flat_forward := -Vector3(cam_basis.z.x, 0.0, cam_basis.z.z).normalized() @@ -120,36 +200,44 @@ func _physics_process(delta: float) -> void: var direction := (flat_forward * -input_dir.y + flat_right * input_dir.x).normalized() var charging := Input.is_action_pressed(&"charge") + var on_floor := is_on_floor() - if direction: - var target_angle := atan2(direction.x, direction.z) + # Reset kick timer on landing so the first kick after a bounce is immediate + if on_floor and not _was_on_floor and direction: + _kick_timer = 0.0 + _was_on_floor = on_floor - if current_speed < 0.01: - facing_angle = target_angle + # ── Periodic kick ───────────────────────────────────────────────────────── + _kick_timer = maxf(0.0, _kick_timer - delta) + if direction and _kick_timer <= 0.0: + _apply_kick(direction, charging) + _kick_timer = DP.f("kick_interval") - var speed_t: float = clampf(current_speed / DP.f("walk_speed"), 0.0, 1.0) - var turn_rate: float = DP.f("charge_turn") if charging \ - else lerpf(DP.f("walk_turn_fast"), DP.f("walk_turn_slow"), speed_t) + # ── Rolling friction (exponential, frame-rate independent) ──────────────── + var retain := pow(DP.f("roll_damping"), delta) + velocity.x *= retain + velocity.z *= retain - var diff := wrapf(target_angle - facing_angle, -PI, PI) - facing_angle += clampf(diff, -turn_rate * delta, turn_rate * delta) - - var top_speed: float = DP.f("charge_speed") if charging else DP.f("walk_speed") - var accel: float = DP.f("charge_accel") if charging else DP.f("walk_accel") - current_speed = move_toward(current_speed, top_speed, accel * delta) - else: - var decel: float = DP.f("charge_decel") if charging else DP.f("deceleration") - current_speed = move_toward(current_speed, 0.0, decel * delta) - - velocity.x = sin(facing_angle) * current_speed - velocity.z = cos(facing_angle) * current_speed - - cube_guy.rotation.y = lerp_angle(cube_guy.rotation.y, facing_angle, - delta * DP.f("visual_turn_speed")) + # ── Visual rotation follows velocity direction ───────────────────────────── + var flat_speed := Vector2(velocity.x, velocity.z).length() + if flat_speed > 0.4: + var target_angle := atan2(velocity.x, velocity.z) + cube_guy.rotation.y = lerp_angle( + cube_guy.rotation.y, target_angle, delta * DP.f("visual_turn_speed")) + _pre_slide_vel_y = velocity.y move_and_slide() - if current_speed > 0.5 and is_on_floor(): - _set_dust_state(_DustState.CHARGE if charging else _DustState.WALK) + # ── Bounce on landing ───────────────────────────────────────────────────── + if is_on_floor() and not _was_on_floor: + var impact := -_pre_slide_vel_y + if impact > DP.f("bounce_min_impact"): + velocity.y = impact * DP.f("bounce_restitution") + + # ── Dust ────────────────────────────────────────────────────────────────── + if flat_speed > DP.f("dust_charge_spd") and on_floor: + _set_dust_state(_DustState.CHARGE) + elif flat_speed > DP.f("dust_walk_spd") and on_floor: + _set_dust_state(_DustState.WALK) else: _set_dust_state(_DustState.NONE) diff --git a/project.godot b/project.godot index fb054ea..5be191f 100644 --- a/project.godot +++ b/project.godot @@ -11,10 +11,14 @@ config_version=5 [application] config/name="Bullosseum" -config/features=PackedStringArray("4.6", "Forward Plus") +config/features=PackedStringArray("4.7", "Forward Plus") config/icon="res://icon.svg" config/run/main_scene="res://scene.tscn" +[editor_plugins] + +enabled=PackedStringArray("res://addons/rider-plugin/plugin.cfg") + [input] move_forward={