From 325588619751304701a6fdfd43cd0ff583ea1620 Mon Sep 17 00:00:00 2001
From: opti1337 <nikita.kruglickiy@gmail.com>
Date: Wed, 30 Oct 2024 02:19:02 +0300
Subject: [PATCH] zxc

---
 Assets/prefabs/player.prefab |  10 +-
 Assets/scenes/minimal.scene  |   5 +-
 Code/AnimationHelper.cs      | 429 +++++++++++++++++++++++++++++++++++
 Code/Kal.cs                  |  45 ++--
 4 files changed, 459 insertions(+), 30 deletions(-)
 create mode 100644 Code/AnimationHelper.cs

diff --git a/Assets/prefabs/player.prefab b/Assets/prefabs/player.prefab
index 8f65d15..c3b9806 100644
--- a/Assets/prefabs/player.prefab
+++ b/Assets/prefabs/player.prefab
@@ -56,12 +56,12 @@
         "WalkSpeed": 100
       },
       {
-        "__type": "Sandbox.Citizen.CitizenAnimationHelper",
-        "__guid": "640af9e4-02ed-4391-95ea-e45fba51fa42",
-        "BodyWeight": 1,
-        "EyesWeight": 1,
+        "__type": "AnimationHelper",
+        "__guid": "536068a6-fe5d-4f45-af74-9522ebf7e663",
+        "BodyWeight": 0,
+        "EyesWeight": 0,
         "HeadWeight": 1,
-        "LookAtEnabled": false,
+        "LookAtEnabled": true,
         "Target": {
           "_type": "component",
           "component_id": "4fd8c36d-180b-41af-8439-6b975ca1c7b3",
diff --git a/Assets/scenes/minimal.scene b/Assets/scenes/minimal.scene
index e27d0ae..ce6d681 100644
--- a/Assets/scenes/minimal.scene
+++ b/Assets/scenes/minimal.scene
@@ -6,7 +6,6 @@
       "Flags": 0,
       "Name": "NetworkManager",
       "Enabled": true,
-      "NetworkMode": 0,
       "Components": [
         {
           "__type": "Sandbox.SceneInformation",
@@ -17,13 +16,13 @@
         },
         {
           "__type": "Sandbox.KPTLConnect",
-          "__guid": "79db0a43-3cee-44bf-86db-fa70ca72f62c",
+          "__guid": "895b0f93-287e-4607-808e-6ce5bdbb5c05",
           "PlayerPrefab": {
             "_type": "gameobject",
             "prefab": "prefabs/player.prefab"
           },
           "SpawnPoints": [],
-          "StartServer": false
+          "StartServer": true
         }
       ]
     },
diff --git a/Code/AnimationHelper.cs b/Code/AnimationHelper.cs
new file mode 100644
index 0000000..9745a39
--- /dev/null
+++ b/Code/AnimationHelper.cs
@@ -0,0 +1,429 @@
+using System;
+
+/// <summary>
+/// Used to control the Citizen animation state. You don't have to use this to animate your citizen avatar, but our
+/// aim is to put everything you need in this class, so you can easily see what variables are available.
+/// </summary>
+[Title( "Animation Helper" )]
+[Category( "KPTL" )]
+[Icon( "directions_run" )]
+public sealed class AnimationHelper : Component, Component.ExecuteInEditor
+{
+	/// <summary>
+	/// The skinned model renderer that we'll apply all parameters to.
+	/// </summary>
+	[Property] public SkinnedModelRenderer Target { get; set; }
+
+	/// <summary>
+	/// Where are the eyes of our character?
+	/// </summary>
+	[Property] public GameObject EyeSource { get; set; }
+
+	/// <summary>
+	/// How tall are we?
+	/// </summary>
+	[Property, Range( 0.5f, 1.5f ), Title( "Avatar Height Scale" )] public float? Height { get; set; }
+
+	/// <summary>
+	/// Are we looking at something? Useful for stuff like cutscenes, where you want an NPC to stare at you.
+	/// </summary>
+	[Property, ToggleGroup( "LookAtEnabled", Label = "Look At" )]
+	public bool LookAtEnabled { get; set; } = false;
+
+	/// <summary>
+	/// Which GameObject should we be looking at?
+	/// </summary>
+	[Property, ToggleGroup( "LookAtEnabled" )] public GameObject LookAt { get; set; }
+
+	[Property, ToggleGroup( "LookAtEnabled" ), Range( 0, 1 )] public float EyesWeight { get; set; } = 1.0f;
+	[Property, ToggleGroup( "LookAtEnabled" ), Range( 0, 1 )] public float HeadWeight { get; set; } = 1.0f;
+	[Property, ToggleGroup( "LookAtEnabled" ), Range( 0, 1 )] public float BodyWeight { get; set; } = 1.0f;
+
+	/// <summary>
+	/// IK will try to place the limb where this GameObject is in the world.
+	/// </summary>
+	[Property, Group( "Inverse kinematics" ), Title( "Left Hand" )] public GameObject IkLeftHand { get; set; }
+
+	/// <inheritdoc cref="IkLeftHand"/>
+	[Property, Group( "Inverse kinematics" ), Title( "Right Hand" )] public GameObject IkRightHand { get; set; }
+
+	/// <inheritdoc cref="IkLeftHand"/>
+	[Property, Group( "Inverse kinematics" ), Title( "Left Foot" )] public GameObject IkLeftFoot { get; set; }
+
+	/// <inheritdoc cref="IkLeftHand"/>
+	[Property, Group( "Inverse kinematics" ), Title( "Right Foot" )] public GameObject IkRightFoot { get; set; }
+	
+	[Sync] private Vector3 LookDir { get; set; }
+
+	protected override void OnUpdate()
+	{
+		if ( !Target.IsValid() )
+			return;
+
+		AimBodyWeight = 0f;
+		AimEyesWeight = 0f;
+		AimHeadWeight = 1f;
+		
+
+		if ( LookAtEnabled )
+		{
+			if ( Network.IsOwner )
+			{
+				LookDir = Scene.Camera.GameObject.WorldRotation.Forward;
+			}
+			WithLook( LookDir, EyesWeight, HeadWeight, BodyWeight );
+		}
+
+		if ( Height.HasValue )
+		{
+			Target.Set( "scale_height", Height.Value );
+		}
+
+		if ( IkLeftHand.IsValid() && IkLeftHand.Active ) Target.SetIk( "hand_left", IkLeftHand.Transform.World );
+		else Target.ClearIk( "hand_left" );
+
+		if ( IkRightHand.IsValid() && IkRightHand.Active ) Target.SetIk( "hand_right", IkRightHand.Transform.World );
+		else Target.ClearIk( "hand_right" );
+
+		if ( IkLeftFoot.IsValid() && IkLeftFoot.Active ) Target.SetIk( "foot_left", IkLeftFoot.Transform.World );
+		else Target.ClearIk( "foot_left" );
+
+		if ( IkRightFoot.IsValid() && IkRightFoot.Active ) Target.SetIk( "foot_right", IkRightFoot.Transform.World );
+		else Target.ClearIk( "foot_right" );
+	}
+
+	public void ProceduralHitReaction( DamageInfo info, float damageScale = 1.0f, Vector3 force = default )
+	{
+		var boneId = info.Hitbox?.Bone?.Index ?? 0;
+		var bone = Target.GetBoneObject( boneId );
+
+		var localToBone = bone.LocalPosition;
+		if ( localToBone == Vector3.Zero ) localToBone = Vector3.One;
+
+		Target.Set( "hit", true );
+		Target.Set( "hit_bone", boneId );
+		Target.Set( "hit_offset", localToBone );
+		Target.Set( "hit_direction", force.Normal );
+		Target.Set( "hit_strength", (force.Length / 1000.0f) * damageScale );
+	}
+
+	/// <summary>
+	/// The transform of the eyes, in world space. This is worked out from EyeSource is it's set.
+	/// </summary>
+	public Transform EyeWorldTransform
+	{
+		get
+		{
+			if ( EyeSource.IsValid() ) return EyeSource.Transform.World;
+
+			return Transform.World;
+		}
+	}
+
+
+	/// <summary>
+	/// Have the player look at this point in the world
+	/// </summary>
+	public void WithLook( Vector3 lookDirection, float eyesWeight = 1.0f, float headWeight = 1.0f, float bodyWeight = 1.0f )
+	{
+		// Target.SetLookDirection( "aim_eyes", lookDirection, eyesWeight );
+		Target.SetLookDirection( "aim_head", lookDirection, headWeight );
+		// Target.SetLookDirection( "aim_body", lookDirection, bodyWeight );
+	}
+
+	/// <summary>
+	/// Have the player animate moving with a set velocity (this doesn't move them! Your character controller is responsible for that)
+	/// </summary>
+	/// <param name="Velocity"></param>
+	public void WithVelocity( Vector3 Velocity )
+	{
+		var dir = Velocity;
+		var forward = Target.WorldRotation.Forward.Dot( dir );
+		var sideward = Target.WorldRotation.Right.Dot( dir );
+
+		var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
+
+		Target.Set( "move_direction", angle );
+		Target.Set( "move_speed", Velocity.Length );
+		Target.Set( "move_groundspeed", Velocity.WithZ( 0 ).Length );
+		Target.Set( "move_y", sideward );
+		Target.Set( "move_x", forward );
+		Target.Set( "move_z", Velocity.z );
+	}
+
+	/// <summary>
+	/// Animates the wish for the character to move in a certain direction. For example, when in the air, your character will swing their arms in that direction.
+	/// </summary>
+	/// <param name="Velocity"></param>
+	public void WithWishVelocity( Vector3 Velocity )
+	{
+		var dir = Velocity;
+		var forward = Target.WorldRotation.Forward.Dot( dir );
+		var sideward = Target.WorldRotation.Right.Dot( dir );
+
+		var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
+
+		Target.Set( "wish_direction", angle );
+		Target.Set( "wish_speed", Velocity.Length );
+		Target.Set( "wish_groundspeed", Velocity.WithZ( 0 ).Length );
+		Target.Set( "wish_y", sideward );
+		Target.Set( "wish_x", forward );
+		Target.Set( "wish_z", Velocity.z );
+	}
+
+	/// <summary>
+	/// Where are we aiming?
+	/// </summary>
+	public Rotation AimAngle
+	{
+		set
+		{
+			value = Target.WorldRotation.Inverse * value;
+			var ang = value.Angles();
+
+			Target.Set( "aim_body_pitch", ang.pitch );
+			Target.Set( "aim_body_yaw", ang.yaw );
+		}
+	}
+
+	/// <summary>
+	/// The weight of the aim angle, but specifically for the Citizen's eyes.
+	/// </summary>
+	public float AimEyesWeight
+	{
+		get => Target.GetFloat( "aim_eyes_weight" );
+		set => Target.Set( "aim_eyes_weight", value );
+	}
+
+	/// <summary>
+	/// The weight of the aim angle, but specifically for the Citizen's head.
+	/// </summary>
+	public float AimHeadWeight
+	{
+		get => Target.GetFloat( "aim_head_weight" );
+		set => Target.Set( "aim_head_weight", value );
+	}
+
+
+	/// <summary>
+	/// The weight of the aim angle, but specifically for the Citizen's body.
+	/// </summary>
+	public float AimBodyWeight
+	{
+		get => Target.GetFloat( "aim_body_weight" );
+		set => Target.Set( "aim_body_weight", value );
+	}
+
+	/// <summary>
+	/// How much the character is rotating in degrees per second, this controls feet shuffling.
+	/// If rotating clockwise this should be positive, if rotating counter-clockwise this should be negative.
+	/// </summary>
+	public float MoveRotationSpeed
+	{
+		get => Target.GetFloat( "move_rotationspeed" );
+		set => Target.Set( "move_rotationspeed", value );
+	}
+
+	[Obsolete( "Use MoveRotationSpeed" )]
+	public float FootShuffle
+	{
+		get => Target.GetFloat( "move_shuffle" );
+		set => Target.Set( "move_shuffle", value );
+	}
+
+	/// <summary>
+	/// The scale of being ducked (crouched) (0 - 1)
+	/// </summary>
+	[Sync] public float DuckLevel
+	{
+		get => Target.GetFloat( "duck" );
+		set => Target.Set( "duck", value );
+	}
+
+	/// <summary>
+	/// How loud are we talking?
+	/// </summary>
+	public float VoiceLevel
+	{
+		get => Target.GetFloat( "voice" );
+		set => Target.Set( "voice", value );
+	}
+
+	/// <summary>
+	/// Are we sitting down?
+	/// </summary>
+	public bool IsSitting
+	{
+		get => Target.GetBool( "b_sit" );
+		set => Target.Set( "b_sit", value );
+	}
+
+	/// <summary>
+	/// Are we on the ground?
+	/// </summary>
+	public bool IsGrounded
+	{
+		get => Target.GetBool( "b_grounded" );
+		set => Target.Set( "b_grounded", value );
+	}
+
+	/// <summary>
+	/// Are we swimming?
+	/// </summary>
+	public bool IsSwimming
+	{
+		get => Target.GetBool( "b_swim" );
+		set => Target.Set( "b_swim", value );
+	}
+
+	/// <summary>
+	/// Are we climbing?
+	/// </summary>
+	public bool IsClimbing
+	{
+		get => Target.GetBool( "b_climbing" );
+		set => Target.Set( "b_climbing", value );
+	}
+
+	/// <summary>
+	/// Are we noclipping?
+	/// </summary>
+	public bool IsNoclipping
+	{
+		get => Target.GetBool( "b_noclip" );
+		set => Target.Set( "b_noclip", value );
+	}
+
+	/// <summary>
+	/// Is the weapon lowered? By default, this'll happen when the character hasn't been shooting for a while.
+	/// </summary>
+	public bool IsWeaponLowered
+	{
+		get => Target.GetBool( "b_weapon_lower" );
+		set => Target.Set( "b_weapon_lower", value );
+	}
+
+	public enum HoldTypes
+	{
+		None,
+		Pistol,
+		Rifle,
+		Shotgun,
+		HoldItem,
+		Punch,
+		Swing,
+		RPG
+	}
+
+	/// <summary>
+	/// What kind of weapon are we holding?
+	/// </summary>
+	public HoldTypes HoldType
+	{
+		get => (HoldTypes)Target.GetInt( "holdtype" );
+		set => Target.Set( "holdtype", (int)value );
+	}
+
+	public enum Hand
+	{
+		Both,
+		Right,
+		Left
+	}
+
+	/// <summary>
+	/// What's the handedness of our weapon? Left handed, right handed, or both hands? This is only supported by some holdtypes, like Pistol, HoldItem.
+	/// </summary>
+	public Hand Handedness
+	{
+		get => (Hand)Target.GetInt( "holdtype_handedness" );
+		set => Target.Set( "holdtype_handedness", (int)value );
+	}
+
+	/// <summary>
+	/// Triggers a jump animation
+	/// </summary>
+	[Broadcast] public void TriggerJump()
+	{
+		Target.Set( "b_jump", true);
+	}
+
+	/// <summary>
+	/// Triggers a weapon deploy animation
+	/// </summary>
+	public void TriggerDeploy()
+	{
+		Target.Set( "b_deploy", true );
+	}
+
+	public enum MoveStyles
+	{
+		Auto,
+		Walk,
+		Run
+	}
+
+	/// <summary>
+	/// We can force the model to walk or run, or let it decide based on the speed.
+	/// </summary>
+	public MoveStyles MoveStyle
+	{
+		get => (MoveStyles)Target.GetInt( "move_style" );
+		set => Target.Set( "move_style", (int)value );
+	}
+
+	public enum SpecialMoveStyle
+	{
+		None,
+		LedgeGrab,
+		Roll,
+		Slide
+	}
+
+	/// <summary>
+	/// We can force the model to have a specific movement state, instead of just running around.
+	/// <see cref="SpecialMoveStyle.LedgeGrab"/> is good for shimmying across a ledge.
+	/// <see cref="SpecialMoveStyle.Roll"/> is good for a platformer game where the character is rolling around continuously.
+	/// <see cref="SpecialMoveStyle.Slide"/> is good for a shooter game or a platformer where the character is sliding.
+	/// </summary>
+	public SpecialMoveStyle SpecialMove
+	{
+		get => (SpecialMoveStyle)Target.GetInt( "special_movement_states" );
+		set => Target.Set( "special_movement_states", (int)value );
+	}
+
+	//Sitting
+	public enum SittingStyle
+	{
+		None,
+		Chair,
+		Floor
+	}
+
+	/// <summary>
+	/// How are we sitting down?
+	/// </summary>
+	public SittingStyle Sitting
+	{
+		get => (SittingStyle)Target.GetInt( "sit" );
+		set => Target.Set( "sit", (int)value );
+	}
+
+	/// <summary>
+	/// How far up are we sitting down from the floor?
+	/// </summary>
+	public float SittingOffsetHeight
+	{
+		get => Target.GetFloat( "sit_offset_height" );
+		set => Target.Set( "sit_offset_height", value );
+	}
+
+	/// <summary>
+	/// From 0-1, how much are we actually sitting down.
+	/// </summary>
+	public float SittingPose
+	{
+		get => Target.GetFloat( "sit_pose" );
+		set => Target.Set( "sit_pose", value );
+	}
+}
diff --git a/Code/Kal.cs b/Code/Kal.cs
index 3e43bf4..d2ce0d8 100644
--- a/Code/Kal.cs
+++ b/Code/Kal.cs
@@ -4,8 +4,8 @@ using ShrimpleCharacterController;
 
 public sealed class Kal : Component
 {
-    [RequireComponent] public ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
-    [RequireComponent] public CitizenAnimationHelper AnimationHelper { get; set; }
+	[RequireComponent] private ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
+	[RequireComponent] private AnimationHelper AnimationHelper { get; set; }
     public SkinnedModelRenderer Renderer { get; set; } 
     [Property] public GameObject Body { get; set; } 
     public GameObject Camera { get; set; }
@@ -16,18 +16,23 @@ public sealed class Kal : Component
     [Property] [Range(100f, 500f, 20f)] public float RunSpeed { get; set; } = 300f;
     [Property] [Range(25f, 100f, 5f)] public float DuckSpeed { get; set; } = 50f;
     [Property] [Range(200f, 500f, 20f)] public float JumpStrength { get; set; } = 350f;
+    
     [Sync] public Angles EyeAngles { get; set; }
-    [Sync] public bool isDucking { get; set; } = false; 
-    [Sync] public bool isRunning { get; set; } = false; 
+    // [Sync] public bool IsDucking { get; set; }
+    // [Sync] public bool IsRunning { get; set; }
 
     protected override void OnStart() 
     {
         base.OnStart();
 
+        Controller = Components.Get<ShrimpleCharacterController.ShrimpleCharacterController>();
+        AnimationHelper = Components.Get<AnimationHelper>();
+
         if ( !Network.IsOwner ) return;
 
         Renderer = Components.Get<SkinnedModelRenderer>(FindMode.EverythingInSelfAndDescendants);
-        Camera = new GameObject(true, "Camera");
+        Camera = Scene.Camera.GameObject;
+        
         Camera.SetParent(GameObject);
         var cameraComponent = Camera.Components.Create<CameraComponent>();
         cameraComponent.ZFar = 32768f;
@@ -40,35 +45,31 @@ public sealed class Kal : Component
         if ( Network.IsOwner )
         {
 	        var wishDirection = Input.AnalogMove.Normal * Rotation.FromYaw( EyeAngles.yaw );
-	        isDucking = Input.Down( "Duck" );
-	        isRunning = Input.Down( "Run" );
-	        var wishSpeed = isDucking ? DuckSpeed :
-		        isRunning ? RunSpeed : WalkSpeed;
+	        var IsDucking = Input.Down( "Duck" );
+	        var IsRunning = Input.Down( "Run" );
+	        var wishSpeed = IsDucking ? DuckSpeed :
+		        IsRunning ? RunSpeed : WalkSpeed;
 
 	        Controller.WishVelocity = wishDirection * wishSpeed;
-
+	        
 	        Controller.Move();
 
 	        if ( Input.Pressed( "Jump" ) && Controller.IsOnGround )
 	        {
 		        Controller.Punch( Vector3.Up * JumpStrength * 2f );
-		        AnimationHelper?.TriggerJump();
+		        AnimationHelper.TriggerJump();
 	        }
-
+	        
 	        if ( !AnimationHelper.IsValid() ) return;
 	        
+	        AnimationHelper.DuckLevel = IsDucking ? 1f : 0f;
         }
-     
-		UpdateAnimations();
         
-    }
-
-    void UpdateAnimations()
-    {
-	    AnimationHelper.WithWishVelocity(Controller.WishVelocity);
-	    AnimationHelper.WithVelocity(Controller.Velocity);
-	    AnimationHelper.IsGrounded = Controller.IsOnGround;
-	    AnimationHelper.DuckLevel = isDucking ? 1f : 0f;
+        Log.Info( AnimationHelper.HeadWeight );
+        
+        AnimationHelper.WithWishVelocity(Controller.WishVelocity);
+        AnimationHelper.WithVelocity(Controller.Velocity);
+        AnimationHelper.IsGrounded = Controller.IsOnGround;
     }
 
     protected override void OnUpdate()