572 lines
19 KiB
C#
572 lines
19 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using EZCameraShake;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.Rendering;
|
|
using Weapons.Projectiles;
|
|
using Random = UnityEngine.Random;
|
|
|
|
/**
|
|
* A script attached to every player to handle visual effects
|
|
*/
|
|
public class PlayerEntity : MonoBehaviour
|
|
{
|
|
[System.NonSerialized] public PlayableCharacterSkin CurrentlyEquippedSkin = null;
|
|
|
|
internal Volume volumeMain;
|
|
internal Volume volumeMashroom;
|
|
internal Material skyMain;
|
|
internal Material skyMashroom;
|
|
|
|
|
|
public Transform FirstPersonCameraPivot;
|
|
[SerializeField] private List<PlayableCharacterSkin> skinList = new List<PlayableCharacterSkin>();
|
|
[SerializeField] private GameObject playerRendererBackgroundObjects;
|
|
|
|
private static RuntimeAnimatorController meleeController;
|
|
private static RuntimeAnimatorController gunController;
|
|
private static RuntimeAnimatorController pistolController;
|
|
private static RuntimeAnimatorController bowController;
|
|
private static RuntimeAnimatorController gloveController;
|
|
|
|
private int playerFlags;
|
|
|
|
//player looking
|
|
private Vector3 currVelocity = Vector3.zero;
|
|
private float lookAngleOffset = 20.0f;
|
|
private float playerLookAngle;
|
|
|
|
//animator caching
|
|
private static readonly int animator_VerticalLook = Animator.StringToHash("VerticalLook");
|
|
private static readonly int animator_xAxis = Animator.StringToHash("xAxis");
|
|
private static readonly int animator_zAxis = Animator.StringToHash("zAxis");
|
|
private static readonly int animator_Magnitude = Animator.StringToHash("VelocityMagnitude");
|
|
|
|
//float caching
|
|
private const float constSmooth = 10.0f;
|
|
|
|
//player rotation
|
|
private Quaternion playerYRotation;
|
|
|
|
//player animation floats
|
|
private float playerAnimX;
|
|
private float playerAnimZ;
|
|
private float playerMagnitude;
|
|
|
|
public string[] playerTextureURLS = new string[1];
|
|
private Texture2D playerSkinTexture;
|
|
private Transform _Skins;
|
|
|
|
[HideInInspector] public PlayerEntity SpectatedPlayer;
|
|
[System.NonSerialized] public Camera spectatedPlayersThirdPersonCamera;
|
|
[System.NonSerialized] public GameObject thirdPersonCameraObject;
|
|
[SerializeField] internal List<Transform> listOfManualInitWeaponslot = new List<Transform>();
|
|
|
|
private void Awake()
|
|
{
|
|
if (meleeController == null)
|
|
meleeController = OnDemandLoader.Load<RuntimeAnimatorController>("Models/Player/Player_MeleeStance_Controller");
|
|
if (gunController == null)
|
|
gunController = OnDemandLoader.Load<RuntimeAnimatorController>("Models/Player/Player_GunStance_Controller");
|
|
if (bowController == null)
|
|
bowController = OnDemandLoader.Load<RuntimeAnimatorController>("Models/Player/Player_bowStance_Controller");
|
|
if (pistolController == null)
|
|
pistolController = OnDemandLoader.Load<RuntimeAnimatorController>("Models/Player/Player_pistolStance_Controller");
|
|
if (gloveController == null)
|
|
gloveController = OnDemandLoader.Load<RuntimeAnimatorController>("Models/Player/Glove_Controller");
|
|
Transform pCanvas = transform.Find("Canvas");
|
|
if (pCanvas != null)
|
|
{
|
|
ChatBubble = pCanvas.Find("CanvasContainer/ChatBubbleContainer/ChatBubble").GetComponent<ChatBubble>();
|
|
Transform nameGOTMPro = pCanvas.Find("CanvasContainer/Name");
|
|
if (nameGOTMPro != null)
|
|
{
|
|
PlayerEntityName = nameGOTMPro.GetComponent<TextMeshProUGUI>();
|
|
}
|
|
}
|
|
_Skins = transform.Find("Skins");
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
animator = gameObject.GetComponent<Animator>();
|
|
|
|
|
|
targetPosition = transform.position;
|
|
lastUpdate = Time.time;
|
|
|
|
if (GetComponent<Player>() == null)
|
|
{
|
|
Destroy(GetComponent<FirstPersonController>());
|
|
Destroy(GetComponent<CharacterController>());
|
|
|
|
PlayerInput input = GetComponent<PlayerInput>();
|
|
input.DeactivateInput();
|
|
if (input != null)
|
|
{
|
|
Destroy(input);
|
|
}
|
|
|
|
Destroy(GetComponent<InputHandler>());
|
|
Destroy(GetComponent<GamePadInputEvents>());
|
|
Destroy(transform.Find("FirstPersonCamera_Holder").gameObject);
|
|
Destroy(transform.Find("PlayerUICamera").gameObject);
|
|
Destroy(GetComponent<ShaderInteractor>());
|
|
Destroy(GetComponent<UICameraPositioner>());
|
|
Destroy(playerRendererBackgroundObjects);
|
|
}
|
|
|
|
InitWeaponSystems();
|
|
|
|
thirdPersonCameraObject = transform.Find("ThirdPerson_Camera").gameObject;
|
|
thirdPersonCameraObject.SetActive(false);
|
|
}
|
|
|
|
internal void InitWeaponSystems()
|
|
{
|
|
GameObject weaponSlot = transform.Find("Root/Hips/Spine_01/Spine_02/Spine_03/Clavicle_R/Shoulder_R/Elbow_R/Hand_R/Weapon").gameObject;
|
|
foreach (Transform weapon in weaponSlot.transform)
|
|
{
|
|
thirdPersonWeaponList.Add(weapon.gameObject);
|
|
}
|
|
|
|
foreach (var weapon in listOfManualInitWeaponslot)
|
|
{
|
|
thirdPersonWeaponList.Add(weapon.gameObject);
|
|
}
|
|
|
|
foreach (GameObject weaponObj in thirdPersonWeaponList)
|
|
{
|
|
thirdPersonWeaponDictionary[int.Parse(weaponObj.name)] = weaponObj;
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
//don't do processing for main player here.. do it in player.cs
|
|
if (id == -1) { return; }
|
|
|
|
//remove Entity if last update is over 5 seconds long.
|
|
if (Time.time - lastUpdate > 5.0f)
|
|
{
|
|
PlayerManager.Instance.RemovePlayer(this);
|
|
return;
|
|
}
|
|
|
|
InterpolateEntity();
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (id == -1) { return; }
|
|
|
|
InterpolateAnimation();
|
|
terrainHeightFix();
|
|
}
|
|
|
|
public void spectatePlayer(int playerId)
|
|
{
|
|
PlayerEntity player = PlayerManager.Instance.GetPlayer(playerId).GetComponent<PlayerEntity>();
|
|
spectatedPlayersThirdPersonCamera = player.thirdPersonCameraObject.GetComponent<Camera>();
|
|
spectatedPlayersThirdPersonCamera.gameObject.SetActive(true);
|
|
}
|
|
|
|
private void InterpolateEntity()
|
|
{
|
|
InterpolatePosition();
|
|
InterpolateRotation();
|
|
}
|
|
|
|
public void ResetJumpTrigger() //used in animation event
|
|
{
|
|
animator.ResetTrigger("Jump");
|
|
}
|
|
|
|
private void InterpolatePosition()
|
|
{
|
|
float distance = Vector3.Distance(transform.position, targetPosition);
|
|
//Debug.Log($"Player: {gameObject.name} is {distance} units away from where they should be!");
|
|
if (distance >= 10f) { transform.position = targetPosition; }
|
|
else { transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref currVelocity, 0.1f); }
|
|
}
|
|
|
|
private void InterpolateRotation()
|
|
{
|
|
Vector3 euler = transform.eulerAngles;
|
|
euler.y = Mathf.LerpAngle(euler.y, playerYRotation.eulerAngles.y, constSmooth * Time.deltaTime);
|
|
transform.eulerAngles = euler;
|
|
}
|
|
|
|
private void InterpolateAnimation()
|
|
{
|
|
animator.SetFloat(animator_VerticalLook, Mathf.Lerp(animator.GetFloat(animator_VerticalLook), playerLookAngle + lookAngleOffset, constSmooth * Time.deltaTime));
|
|
animator.SetFloat(animator_xAxis, Mathf.Lerp(animator.GetFloat(animator_xAxis), playerAnimX, constSmooth * Time.deltaTime));
|
|
animator.SetFloat(animator_zAxis, Mathf.Lerp(animator.GetFloat(animator_zAxis), playerAnimZ, constSmooth * Time.deltaTime));
|
|
animator.SetFloat(animator_Magnitude, Mathf.Lerp(animator.GetFloat(animator_Magnitude), playerMagnitude, constSmooth * Time.deltaTime));
|
|
|
|
if ((playerFlags & 0x02) == 0x02) { animator.SetTrigger("Jump"); }
|
|
animator.SetBool("Aiming", (playerFlags & 0x01) == 0x01);
|
|
}
|
|
|
|
public void ProcessUpdateMessage(int id, float x, float y, float z, float rotationY, float lookAngle, string name, float animX, float animZ, float magnitude, int flags, string forceAnim)
|
|
{
|
|
|
|
if (PlayerEntityName != null)
|
|
{
|
|
Debug.Log(PlayerEntityName);
|
|
PlayerEntityName.text = name;
|
|
playerName = name;
|
|
}
|
|
|
|
lastPacketPosition = transform.position;
|
|
|
|
targetPosition = new Vector3(x, y, z);
|
|
|
|
velocity = targetPosition - lastPacketPosition;
|
|
|
|
//playerYRotation is cached rotation and calculated outside of the 10 updates a second step.
|
|
playerYRotation = Quaternion.Euler(transform.rotation.eulerAngles.x, rotationY, transform.rotation.eulerAngles.z);
|
|
playerLookAngle = lookAngle;
|
|
|
|
//animation interpolation caching
|
|
playerAnimX = animX;
|
|
playerAnimZ = animZ;
|
|
playerMagnitude = magnitude;
|
|
playerFlags = flags;
|
|
|
|
if (forceAnim.Length > 0)
|
|
{
|
|
animator.Play(forceAnim);
|
|
}
|
|
lastUpdate = Time.time;
|
|
}
|
|
|
|
public void setWeapon(int itemId)
|
|
{
|
|
Weapon.WEAPON_TYPE weaponType = ItemDef.GetInstance().getType(itemId);
|
|
lookAngleOffset = 20.0f;
|
|
switch (weaponType)
|
|
{
|
|
case Weapon.WEAPON_TYPE.NONE:
|
|
break;
|
|
case Weapon.WEAPON_TYPE.GUN:
|
|
animator.runtimeAnimatorController = gunController;
|
|
break;
|
|
case Weapon.WEAPON_TYPE.BOW:
|
|
animator.runtimeAnimatorController = bowController;
|
|
break;
|
|
case Weapon.WEAPON_TYPE.SWORD:
|
|
lookAngleOffset = 20f; //animations are angled down more. so decrease the offset.
|
|
animator.runtimeAnimatorController = meleeController;
|
|
break;
|
|
case Weapon.WEAPON_TYPE.PISTOL:
|
|
lookAngleOffset = 5f; //animations are angled down more. so decrease the offset.
|
|
animator.runtimeAnimatorController = pistolController;
|
|
break;
|
|
case Weapon.WEAPON_TYPE.MINER:
|
|
lookAngleOffset = 5f;
|
|
animator.runtimeAnimatorController = meleeController;
|
|
break;
|
|
case Weapon.WEAPON_TYPE.GLOVE:
|
|
animator.runtimeAnimatorController = gloveController;
|
|
lookAngleOffset = 5f;
|
|
break;
|
|
}
|
|
|
|
|
|
this.weaponId = itemId;
|
|
this.projectileOrigin = null;
|
|
|
|
GameObject weaponSlot = transform.Find("Root/Hips/Spine_01/Spine_02/Spine_03/Clavicle_R/Shoulder_R/Elbow_R/Hand_R/Weapon").gameObject;
|
|
foreach (Transform child in weaponSlot.transform)
|
|
{
|
|
child.gameObject.SetActive(false);
|
|
}
|
|
|
|
foreach (var manualSlots in listOfManualInitWeaponslot)
|
|
{
|
|
manualSlots.gameObject.SetActive(false);
|
|
}
|
|
|
|
Debug.Log($"Activate {itemId}");
|
|
if (itemId != -1)
|
|
{
|
|
if (thirdPersonWeaponDictionary.TryGetValue(itemId, out var valWeapon))
|
|
{
|
|
valWeapon.SetActive(true);
|
|
currentWeapon = valWeapon;
|
|
SetLayerRecursively(currentWeapon, "Default");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetLayerRecursively(GameObject obj, string newLayer)
|
|
{
|
|
if (obj == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj.layer = LayerMask.NameToLayer(newLayer);
|
|
|
|
foreach (Transform child in obj.transform)
|
|
{
|
|
if (child == null)
|
|
{
|
|
continue;
|
|
}
|
|
SetLayerRecursively(child.gameObject, newLayer);
|
|
}
|
|
}
|
|
|
|
public IEnumerator processWeaponFire(Vector3 direction, string projectile)
|
|
{
|
|
if (ItemDef.GetInstance().isThrowable(weaponId))
|
|
{
|
|
animator.Play("3D_ThrowNade");
|
|
yield return new WaitForSeconds(0.7f);
|
|
fireProjectile(direction, projectile);
|
|
}
|
|
else
|
|
{
|
|
fireProjectile(direction, projectile);
|
|
animator.Play("3D_Shoot");
|
|
}
|
|
}
|
|
|
|
public Vector3 GetProjectileOrigin()
|
|
{
|
|
if (projectileOrigin)
|
|
return projectileOrigin.transform.position;
|
|
else
|
|
{
|
|
return Vector3.zero;;
|
|
}
|
|
}
|
|
|
|
public void SetSkin(string skin)
|
|
{
|
|
//Debug.Log("setting skin " + skin);
|
|
PlayableCharacterSkin currentlyEquiptSkin = _Skins.Find(skin).GetComponent<PlayableCharacterSkin>();
|
|
CurrentlyEquippedSkin = currentlyEquiptSkin;
|
|
|
|
for (int i = 0; i < skinList.Count; i++)
|
|
{
|
|
//Debug.Log($"Searching for {skins[i]}- result: {transform.Find(skins[i])}");
|
|
try
|
|
{
|
|
skinList[i].DeactivateSkin();
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.Log(e.ToString());
|
|
}
|
|
}
|
|
|
|
currentlyEquiptSkin.ActivateSkin();
|
|
|
|
|
|
if (GetComponent<Player>() != null && UIManager.Instance.IsGameLobbyCanvasesActive())
|
|
{
|
|
//client side stats. Rest are server side.
|
|
SetSpecialStat(skin);
|
|
}
|
|
}
|
|
|
|
private void SetSpecialStat(string skin)
|
|
{
|
|
PreGameManager.Instance.SetSpecialStat(skin);
|
|
}
|
|
|
|
public IEnumerator GetAndSetPlayerTexture(GameObject mainPlayer)
|
|
{
|
|
int randTexture = Random.Range(0, 5);
|
|
UnityWebRequest www = UnityWebRequestTexture.GetTexture(playerTextureURLS[0]);
|
|
yield return www.SendWebRequest();
|
|
|
|
playerSkinTexture = DownloadHandlerTexture.GetContent(www);
|
|
//Debug.Log("Decal successfully Loaded!" + texture);
|
|
|
|
foreach (SkinnedMeshRenderer playerModel in mainPlayer.GetComponentsInChildren<SkinnedMeshRenderer>())
|
|
{
|
|
if (!playerModel.enabled || !playerModel.gameObject.name.Contains("Character_")) { continue; }
|
|
//Debug.Log($"{playerModel.gameObject.name} did not contain \"Character_\" and enable was {playerModel.enabled}");
|
|
playerModel.material.SetTexture("_BaseMap", playerSkinTexture);
|
|
}
|
|
}
|
|
public bool IsFuturisticWeapon()
|
|
{
|
|
if (weaponId == 0 || weaponId == 4 || weaponId == 5 || weaponId == 6 || weaponId == 12)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void fireProjectile(Vector3 direction, string projectile)
|
|
{
|
|
if (projectile == null)
|
|
return;
|
|
|
|
if (projectileOrigin == null)
|
|
return;
|
|
var qTargetRotateProjectile = Quaternion.identity;
|
|
var isFlameProjectile = false;
|
|
|
|
if (projectile == "Level_1_FlameAttack"||
|
|
projectile == "Level_2_FlameAttack"||
|
|
projectile == "Level_3_FlameAttack"||
|
|
projectile == "Level_4_FlameAttack"||
|
|
projectile == "Level_5_FlameAttack"||
|
|
projectile == "Level_6_FlameAttack"||
|
|
projectile == "Level_7_FlameAttack"
|
|
)
|
|
{
|
|
isFlameProjectile = true;
|
|
qTargetRotateProjectile = projectileOrigin.transform.rotation;
|
|
}
|
|
|
|
GameObject pooledProjectile = null;
|
|
if (!isFlameProjectile)
|
|
pooledProjectile = NetworkPooler.Instance.PooledInstantiate(projectile, projectileOrigin.transform.position,qTargetRotateProjectile);
|
|
if (projectile != "Grenade" && !isFlameProjectile)
|
|
{
|
|
pooledProjectile.GetComponent<ProjectileBase>().ApplyForce(direction);
|
|
}
|
|
else if (isFlameProjectile)
|
|
{
|
|
var current = thirdPersonWeaponDictionary[weaponId];
|
|
projectileOrigin = current.transform.Find("projectileOrigin")?.gameObject;
|
|
projectileOrigin.transform.GetComponent<FlameProjectile>().InstantiateOrigin(this, projectileOrigin.transform, projectile);
|
|
}
|
|
else
|
|
{
|
|
pooledProjectile.GetComponent<Projectile>().ApplyForce(direction, 10);
|
|
}
|
|
|
|
if (id != -1)
|
|
{
|
|
//only create muzzleflashes for none-main player. Main player muzzleflash are handled seperately.
|
|
if (IsFuturisticWeapon())
|
|
{
|
|
NetworkPooler.Instance.PooledPlayEffect("FuturisticMuzzleFlash", projectileOrigin.transform.position, projectileOrigin.transform.rotation, 3.0f);
|
|
}
|
|
else
|
|
{
|
|
//Debug.Log("Not a futuristic weapon,");
|
|
//exclude rocket launcher and grenade from the muzzleflash. We can make it have it if neeed. Remove this if check.
|
|
if (weaponId != 14 && weaponId != 23)
|
|
{
|
|
//Debug.Log("Playing MuzzleFlash!");
|
|
NetworkPooler.Instance.PooledPlayEffect("NormalMuzzleFlash", projectileOrigin.transform.position, projectileOrigin.transform.rotation, 3.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if bullet didnt hit anything. Disable it after 10 seconds.
|
|
NetworkPooler.Instance.PooledDestroy(projectile, pooledProjectile, 10.0f);
|
|
}
|
|
|
|
private void terrainHeightFix()
|
|
{
|
|
Terrain terrain = Misc.GetClosestCurrentTerrain(transform.position);
|
|
float height = terrain.SampleHeight(transform.position);
|
|
if (transform.position.y < height)
|
|
{
|
|
transform.position = new Vector3(transform.position.x, height, transform.position.z);
|
|
}
|
|
}
|
|
|
|
public int GetID()
|
|
{
|
|
return id;
|
|
}
|
|
|
|
public void SetID(int id)
|
|
{
|
|
this.id = id;
|
|
}
|
|
|
|
public string GetVoiceID()
|
|
{
|
|
return voiceID;
|
|
}
|
|
public void SetVoiceID(string voiceID)
|
|
{
|
|
this.voiceID = voiceID;
|
|
|
|
}
|
|
|
|
public int GetSkinID()
|
|
{
|
|
return skinList.IndexOf(CurrentlyEquippedSkin);
|
|
}
|
|
|
|
public void SetLastPositionAfterTeleportation(Vector3 vector3)
|
|
{
|
|
lastPacketPosition = vector3;
|
|
targetPosition = vector3;
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
{
|
|
if (other.CompareTag("PPdetector"))
|
|
{
|
|
if (id == -1)
|
|
{
|
|
OnDetectPP?.Invoke();
|
|
volumeMain.gameObject.SetActive(false);
|
|
volumeMashroom.gameObject.SetActive(true);
|
|
RenderSettings.skybox = skyMashroom;
|
|
|
|
}
|
|
}
|
|
}
|
|
private void OnTriggerExit(Collider other)
|
|
{
|
|
if (other.CompareTag("PPdetector"))
|
|
{
|
|
if (id == -1)
|
|
{
|
|
OnDetectClosePP?.Invoke();
|
|
volumeMain.gameObject.SetActive(true);
|
|
volumeMashroom.gameObject.SetActive(false);
|
|
|
|
RenderSettings.skybox = skyMain;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public string GetName() => playerName;
|
|
|
|
public static event Action OnDetectPP;
|
|
public static event Action OnDetectClosePP;
|
|
|
|
[HideInInspector] public GameObject projectileOrigin;
|
|
[HideInInspector] public int weaponId;
|
|
public ChatBubble ChatBubble { get; internal set; }
|
|
|
|
private int id = -1;
|
|
private string voiceID = "null";
|
|
[HideInInspector] public Animator animator;
|
|
private float lastUpdate;
|
|
private Vector3 lastPacketPosition;
|
|
private Vector3 targetPosition;
|
|
private Vector3 velocity;
|
|
[System.NonSerialized] public TextMeshProUGUI PlayerEntityName;
|
|
private string playerName;
|
|
|
|
[HideInInspector] public GameObject currentWeapon;
|
|
private List<GameObject> thirdPersonWeaponList = new List<GameObject>();
|
|
private readonly Dictionary<int, GameObject> thirdPersonWeaponDictionary = new Dictionary<int, GameObject>();
|
|
|
|
}
|
|
|