using DamageNumbersPro; using Photon.Pun; using Photon.Realtime; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Animations.Rigging; using static UnityEngine.EventSystems.EventTrigger; public class PlayerNetwork : MonoBehaviourPunCallbacks, IPunObservable { public string username; public string partyname; public string partyId; public Rig weaponRig; public Rig aimRig; public Animator anim; public RuntimeAnimatorController defaultAnim; public Transform lookAtTarget; public List weapons; public List nades; public int selectedWeaponIndex = -1; public int selectedNadeIndex=-1; [Range(0,100f)] public float health = 100; [Range(0,100f)] public float shield = 100; public int kills; public int deaths; public int assists; PlayerFX playerFX; public AnimationCurve DistanceDamageCurve; public float BulletEffectiveRange; List hitboxes = new List(); public override void OnPlayerEnteredRoom(Player newPlayer) { base.OnPlayerEnteredRoom(newPlayer); AutoAssignParty(); } public override void OnPlayerLeftRoom(Player otherPlayer) { base.OnPlayerLeftRoom(otherPlayer); AutoAssignParty(); } public void RegisterHitbox(HitBox hitBox){ if(hitboxes == null){hitboxes = new List();} hitboxes.Add(hitBox); } void Awake() { playerController = GetComponent(); playerFX = GetComponent(); } void Start() { if (photonView.IsMine) { photonView.RPC("RpcSyncName", RpcTarget.AllBuffered ,string.IsNullOrEmpty(LoginManager.username) ? "Dev0" : LoginManager.username); UIManager.SetHealth(health,shield); UIManager.SetUsername( username); // if(GameManager.desiredGameMode== GameModes.TeamDeathmatch.ToString()){ignoreFriendlyFire=true;} } GetComponent().detectCollisions=false; } public void AutoAssignParty(){ StartCoroutine(AutoSwitchTeams()); } void autoAssignParty(){ partyId = GameManager.partyId; List availablePartyNames = Helpers.battleTeamNames.ToList(); Dictionary parties = new Dictionary(); PlayerNetwork[] players = FindObjectsOfType(); string myLobbyParty = ""; foreach(PlayerNetwork player in players){ if(player.partyname != ""){ //has a party if(parties.ContainsKey(player.partyname)){ parties[player.partyname]++; }else{ parties.Add(player.partyname, 1); } if(availablePartyNames.Contains(player.partyname)){availablePartyNames.Remove(player.partyname);} if(partyId == player.partyId){ myLobbyParty = player.partyname; } } } Debug.Log($"Found {parties.Count} Parties"); if(myLobbyParty == ""){ //Didn't find a party of my own if(parties.Count > 1){ foreach(KeyValuePair party in parties){ if(party.Value < 4){ partyname = party.Key; break; } } }else{ partyname = availablePartyNames[UnityEngine.Random.Range(0, availablePartyNames.Count)]; } }else{ if(parties[myLobbyParty] < 4){ partyname = myLobbyParty; }else{ foreach(KeyValuePair party in parties){ if(party.Value < 4){ partyname = party.Key; break; } } } } if(partyname == ""){ partyname = availablePartyNames[UnityEngine.Random.Range(0, availablePartyNames.Count)]; } } int autoTeamSwitchCount = 0; IEnumerator AutoSwitchTeams(){ while(autoTeamSwitchCount < 4){ autoTeamSwitchCount++; yield return new WaitForSeconds(UnityEngine.Random.Range(0.5f,1f)); autoAssignParty(); } } public Dictionary GetPartySizes(){ Dictionary parties = new Dictionary(); foreach(PlayerNetwork player in FindObjectsOfType()){ if(player.partyname != ""){ //has a party if(parties.ContainsKey(player.partyname)){ parties[player.partyname]++; }else{ parties.Add(player.partyname, 1); } } } return parties; } public void SetActiveWeapon(LootItemScriptableObject data) { int _selectedIndex = -1; int _selectedNadeIndex =-1; if(data == null){ selectedNadeIndex = _selectedNadeIndex; selectedWeaponIndex = _selectedIndex; return; } if(data.isThrowable){ _selectedIndex=-1; for(int i=0;i < nades.Count; i++){ if(nades[i].data == data){ _selectedNadeIndex=i; break; } } }else{ _selectedNadeIndex=-1; for(int i=0; i < weapons.Count; i++) { if (weapons[i].data == data) { _selectedIndex = i; UpdatePlayerFX(weapons[i]); break; } } } selectedNadeIndex =_selectedNadeIndex; selectedWeaponIndex = _selectedIndex; } [PunRPC] void RpcSyncName(string name) { username = name; } [PunRPC] void RpcSyncPartyName(string name,string id){ partyname = name; partyId = id; } void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.IsWriting) { stream.SendNext(lookAtTarget.position); stream.SendNext(weaponRig.weight); stream.SendNext(aimRig.weight); stream.SendNext(weaponRig.weight); stream.SendNext(selectedWeaponIndex); stream.SendNext(selectedNadeIndex); stream.SendNext(health); stream.SendNext(shield); stream.SendNext(kills); stream.SendNext(deaths); stream.SendNext(assists); stream.SendNext(partyId); stream.SendNext(partyname); } else { lookAtTarget.position = Vector3.Lerp(lookAtTarget.position, (Vector3)stream.ReceiveNext(),0.5f); weaponRig.weight = (float)stream.ReceiveNext(); aimRig.weight= (float)stream.ReceiveNext(); weaponRig.weight= (float)stream.ReceiveNext(); selectedWeaponIndex =((int)stream.ReceiveNext()); selectedNadeIndex = ((int)stream.ReceiveNext()); health = (float)stream.ReceiveNext(); shield = (float)stream.ReceiveNext(); kills = (int)stream.ReceiveNext(); deaths = (int)stream.ReceiveNext(); assists = (int)stream.ReceiveNext(); partyId = (string)stream.ReceiveNext(); partyname = (string)stream.ReceiveNext(); if(prevActiveWeapon != selectedWeaponIndex || prevActiveNade != selectedNadeIndex){ UpdateActiveWeapon(); } prevActiveWeapon = selectedWeaponIndex; prevActiveNade = selectedNadeIndex; } } float prevActiveWeapon =0; float prevActiveNade=0; void UpdateActiveWeapon() { for(int i=0; i < weapons.Count; i++) { weapons[i].obj.SetActive(i == selectedWeaponIndex); weapons[i].muzzleFlash.SetActive(false); if(selectedWeaponIndex == i) { UpdatePlayerFX(weapons[i]); } } for(int i=0; i < nades.Count; i++){ nades[i].obj.SetActive(i==selectedNadeIndex); } } public LootItemScriptableObject CurrentWeaponData; void UpdatePlayerFX(PlayerWeaponEntry data) { playerFX.MuzzleFlash = data.muzzleFlash; playerFX.FireSFXs = data.FireSFX; // playerFX.fireSFX = data.FireSFX; if(!photonView.IsMine){ if(data.data.overrideController == null){ anim.runtimeAnimatorController = defaultAnim; }else{ anim.runtimeAnimatorController = data.data.overrideController; } } } public void OnNadeThrow(){ photonView.RPC("RpcOnNadeThrow", RpcTarget.All); } [PunRPC] void RpcOnNadeThrow(){ if(!photonView.IsMine){ anim.SetTrigger("Throw"); } } PlayerController playerController; public void OnHit(int id, HitBoxType type, Vector3 point, float dist) { float damage = PlayerControllerHelper.GetDamageForHitboxType(type) *GetDamageMultiplier(dist); photonView.RPC("RpcOnHit", RpcTarget.All, id, damage,point); } public void OnHit(int id, float damage,Vector3 point,float dist) { photonView.RPC("RpcOnHit", RpcTarget.All, id, damage * GetDamageMultiplier(dist), point); } public float GetDamageMultiplier(float dist){ if(!GameManager.isInit){return 0;} return DistanceDamageCurve.Evaluate(Mathf.Clamp01(dist/BulletEffectiveRange)); } public bool ignoreFriendlyFire; [PunRPC] void RpcOnHit(int id, float damage, Vector3 point) { Debug.Log($"{username} ({photonView.ViewID}) Got hit by user ID {id})"); float newShield = shield; float newHealth = health; if(ignoreFriendlyFire){ PlayerNetwork[] players = FindObjectsOfType(); foreach(PlayerNetwork p in players){ if(p.photonView.ViewID == id){ //This is the shooter if(partyname == p.partyname){return;} //Friendly fire } } } if(shield > 0){ newShield = Mathf.Clamp(shield - damage,0,100); }else{ newHealth = health-damage; } if (photonView.IsMine) { playerController.OnHit(damage); UIManager.instance.OnScreenDamageFx.color = new Color(1, 1, 1, UIManager.instance.OnScreenDamageFx.color.a + (damage / 100f)); GameObject enemy = PhotonView.Find(id).gameObject; if(newHealth <= 0 && health >0) { //Death bool shouldSpawnLoot = GameManager.desiredGameMode != GameModes.Deathmatch.ToString(); //Spawn loots if(shouldSpawnLoot){ float a= 0; foreach(KeyValuePair item in InventoryManager.instance.Stock){ a += Mathf.PI * 0.2f; float x = Mathf.Sin(a); float y = Mathf.Cos(a); GameManager.instance.SpawnPickup(item.Key.spawnableName, transform.position + new Vector3(x,0,y), count: item.Value); } if(InventoryManager.instance.FirstWeapon != null){ a += Mathf.PI * 0.2f; float x = Mathf.Sin(a); float y = Mathf.Cos(a); GameManager.instance.SpawnPickup(InventoryManager.instance.FirstWeapon.spawnableName,point: transform.position + new Vector3(x,0,y)); } if(InventoryManager.instance.SecondWeapon != null){ a += Mathf.PI * 0.2f; float x = Mathf.Sin(a); float y = Mathf.Cos(a); GameManager.instance.SpawnPickup(InventoryManager.instance.SecondWeapon.spawnableName,point: transform.position + new Vector3(x,0,y)); } if(InventoryManager.instance.PrimaryWeapon != null){ a += Mathf.PI * 0.2f; float x = Mathf.Sin(a); float y = Mathf.Cos(a); GameManager.instance.SpawnPickup(InventoryManager.instance.PrimaryWeapon.spawnableName,point: transform.position + new Vector3(x,0,y)); } } InventoryManager.ResetStocks(); CameraFollow.Pause(); enemy.GetComponent().GainedKill(); //Destroy(GetComponent()); playerController.OnDeath(); UIManager.OnDeath(); deaths++; if(GameManager.desiredGameMode == GameModes.Deathmatch.ToString()){ StartCoroutine(CoroutineRespawner()); } UserDataManager.instance.AddDeath(); // PhotonNetwork.Destroy(gameObject); } UIManager.SetHealth(newHealth,newShield); Vector3 toPosition = (enemy.transform.position - Camera.main.transform.position).normalized; float angle = Vector3.SignedAngle(Camera.main.transform.forward, toPosition, Vector3.up); UIManager.SetDamageDirection(angle); }else{ } if(newHealth <=0){ //Dead general anim.SetBool("dead",true); } health=newHealth; shield= newShield; if(GameManager.localPlayer.photonView.ViewID == id && health >0) { Debug.Log("Show damage number"); //I HIT THIS GUY! playerFX.ShowDamageNumber(point, damage); } } IEnumerator CoroutineRespawner(){ int seconds = 0; while(seconds < 4){ yield return new WaitForSeconds(1); seconds++; //TODO: Update timer text here } Respawn(); } public void Respawn(){ if(photonView.IsMine){ respawn(); }else{ photonView.RPC("RpcRespawn",RpcTarget.All); } } [PunRPC] void RpcRespawn(){ respawn(); } void respawn(){ Transform newSpawnPoint = GameManager.instance.GetRandomSpawnPoint(); transform.position = newSpawnPoint.position; anim.SetBool("dead",false); health = 100; shield= 100; UIManager.SetHealth(health,shield); CameraFollow.Resume(); playerController.isActive=true; UIManager.OnRespawn(); if(GameManager.desiredGameMode == GameModes.Deathmatch.ToString() || GameManager.desiredGameMode == GameModes.TeamDeathmatch.ToString()){ InventoryManager.instance.GiveDefaultLoots(GameManager.instance.DeathmatchDefaultInventory); } UIManager.UpdateInventory(InventoryManager.instance); } public void GainedKill(){ if(photonView.IsMine){ gainedKill(); }else{ photonView.RPC("RpcGainKill", RpcTarget.All); } } [PunRPC] void RpcGainKill(){ if(photonView.IsMine){ gainedKill();} } void gainedKill(){ Debug.Log("Got a new kill"); UserDataManager.instance.AddKill(); kills++; } public void StartBomb(int pid, GameObject nade, LootItemScriptableObject data) { StartCoroutine(TickBomb(pid,nade, data)); } IEnumerator TickBomb(int pid, GameObject nade, LootItemScriptableObject data) { yield return new WaitForSeconds(data.throwableTimer); photonView.RPC("RpcBlastEffect", RpcTarget.All, photonView.ViewID, nade.transform.position, data.spawnableName); //Handle Nade damage PlayerNetwork[] players = FindObjectsOfType(); foreach(PlayerNetwork player in players){ player.photonView.RPC("RpcHandleNadeDamage", RpcTarget.All, nade.transform.position, photonView.ViewID, data.throwableDamage, data.throwableDamageRange); } PhotonNetwork.Destroy(nade); } [PunRPC] void RpcBlastEffect(int thrower, Vector3 blastPosition, string lootSpawnableName){ LootItemScriptableObject data = GameManager.GetLootByName(lootSpawnableName); ObjectsPool.instance.Spawn(data.throwableBlastEffect, blastPosition, Quaternion.identity, effect: true); float distToBomb = Vector3.Distance(transform.position, blastPosition); Debug.Log($"{username}({photonView.ViewID}) was {distToBomb} away from grenade thrown by {thrower}"); } [PunRPC] void RpcHandleNadeDamage(Vector3 blastPosition, int thrower, float damage, float range){ float distToBomb = Vector3.Distance(transform.position, blastPosition); Debug.Log($"{username}({photonView.ViewID}) was {distToBomb} away from grenade thrown by {thrower}"); if(photonView.IsMine){ if(distToBomb < range){ float intensity = (range - distToBomb) * damage; OnHit(thrower, intensity, transform.position,0); CameraFollow.Shake(intensity * 0.01f); }else{ Debug.Log($"Bomb was {distToBomb} far away"); } } } //Testing Damage Direction /* float a; void Update() { a += Time.deltaTime; float x = Mathf.Sin(a) * 100; float y = Mathf.Cos(a) * 100; Vector3 newPos = transform.position + new Vector3(x, 0, y); Debug.DrawLine(transform.position, newPos); Vector3 toPosition = (newPos - Camera.main.transform.position).normalized; float angle = Vector3.SignedAngle(Camera.main.transform.forward, toPosition, Vector3.up); Debug.Log(angle); UIManager.SetDamageDirection(angle); }*/ } [Serializable] public struct PlayerWeaponEntry { public LootItemScriptableObject data; public GameObject obj; public GameObject muzzleFlash; public AudioClip[] FireSFX; public Transform leftHandIk; } [Serializable] public struct LootObjectEntry{ public LootItemScriptableObject data; public GameObject obj; }