using System.Collections; using System.Collections.Generic; using Assets.HeroEditor4D.Common.Scripts.CharacterScripts; using Assets.HeroEditor4D.Common.Scripts.Enums; using Mirror; using TMPro; using Firebase.Firestore; using Firebase.Extensions; using Unity.VisualScripting; using UnityEngine; using UnityEngine.SceneManagement; using GooglePlayGames; using UnityEngine.SocialPlatforms; using Firebase.Auth; using UnityEngine.UI; using Newtonsoft.Json; public class playerNetwork : NetworkBehaviour { public static playerNetwork localPlayer; public invitePlayer invitePlayer; public const float ATTACK_COOLDOWN = 0.6f; [HideInInspector] public StatManager statManager; //public const int XPFORLEVEL = 10; [SyncVar(hook = nameof(OnHealthChanged))] public int health = 100; public Character4D character; public characterManager characterMan; [SyncVar(hook = nameof(OnDirectionChanged))] public Vector2 directionNetwork; [SyncVar(hook = nameof(OnAnimChanged))] public int animIntNetwork; [SyncVar(hook = nameof(onKillCountChange))] public int enemyKillCount; // public int xp { get{ // int val = 0; // for(int i =5; i <= enemyKillCount; i+=5){ // val = enemyKillCount; // } // return val; // }} [SyncVar(hook = nameof(OnXpChanged))] public int XP; [SyncVar] public string myPartyOwner; public int lvl2 { get { return GetLevelForKills2(enemyKillCount); } } public int GetLevelForKills2(int kills) { int val = 0; for (int i = 10; i <= kills; i += 10) { val++; } return val; } public int lvl { get { return GetLevelByXp(XP); } } public int GetLevelByXp(int xp) { // int level = Mathf.CeilToInt(Mathf.Sqrt(xp/100f)); // if(level <= 0){level = 1;} // return level; int level = Mathf.CeilToInt(Mathf.Log(xp / 100f, 2) + 1); if (level <= 0) { level = 1; } return level; // int level = 1; // int xpNeeded = 100; // while (xp >= xpNeeded) // { // level++; // xpNeeded = 100 * (int)Mathf.Pow(2, level - 1); // } // return level - 1; } public int GetXpForLevel(int level) { if (level <= 0) level = 1; return Mathf.FloorToInt(100 * Mathf.Pow(2, level - 1)); } public float XpSliderVal { get { int nextLevelXp = GetXpForLevel(lvl); int prevLevelXp = GetXpForLevel(lvl - 1); if (nextLevelXp == prevLevelXp) { prevLevelXp = 0; } int range = nextLevelXp - prevLevelXp; // Debug.Log($"{XP-prevLevelXp} / {range}"); //Debug.Log($"next : {nextLevelXp}, prev: {prevLevelXp}, xp:{XP}, xpSince:{XP-prevLevelXp}"); return ((float)(XP - prevLevelXp) / (float)range); // int nextLevel = lvl+1; // int curLevelXp = lvl * lvl; // int nextLevelXp = nextLevel * nextLevel; // // Debug.Log($"Xp Calc: {XP}-{curLevelXp}/{nextLevelXp} - {curLevelXp}"); // // Debug.Log($"{curLevelXp-XP}/{nextLevelXp - curLevelXp}"); // if(XP == 0){return 0;} // return 1f - ((float)(curLevelXp - XP) / (float)(nextLevelXp- curLevelXp)); } } [SyncVar] public string playerName; [SyncVar] public int playerCoin; [SyncVar] public string myCharJson; public TMP_Text txtEnemyKillCount; public TMP_Text txtPlayerName; public TMP_Text questText; public GameObject questUI; public TMP_Text coinText; public TMP_Text xpText; public TMP_Text lvlText; public Slider xpSlider; public GameObject[] xpSliderSlots; public TMP_Text xpEnableTxt; //public TMP_Text attackDmgEnableTxt; public GameObject canvas; public Slider healthBar; public Slider armorBar; public Inventory inventory; public static Transform localPlayerTransform; public GameObject healthVfx; public GameObject damageVfx; public GameObject levelUpVfx; public QuestScriptable currentQuest; public List questActions; public List completedQuests; public GameObject projectile; public GameObject arrowRange; public string selectedCharacterJson = CharacterSelection.selectedCharJson; public void SetActiveQuest(QuestScriptable questData) { currentQuest = questData; questText.text = questData.questTitle; questUI.SetActive(true); foreach (QuestAction quest in questActions) { if (quest.questData == questData) { quest.activate(); } } } public void CompleteQuest(QuestScriptable questData) { if (questData != currentQuest) { Debug.LogError("Completed a quest that wasnt active"); return; } completedQuests.Add(currentQuest.questName); currentQuest = null; questText.text = "Quest Completed!"; playerCoin += questData.rewardAmount; coinText.text = playerCoin.ToString(); //add delay StartCoroutine(DelayUI()); } public void CancelledQuest() { questText.text = "Quest Cancelled: " + currentQuest.questTitle; // currentQuest = null; StartCoroutine(DelayUI()); } IEnumerator DelayUI() { yield return new WaitForSecondsRealtime(10f); questUI.SetActive(false); } public static void registerQuestAction(QuestAction action) { localPlayerTransform.GetComponent().questActions.Add(action); } void Awake() { invitePlayer = GetComponent(); rangeEnemyFind = GetComponent(); } void Start() { #if UNITY_EDITOR if (isServer) { playerName = "Player" + Random.Range(0, 100); } #endif if (!isLocalPlayer) { canvas.SetActive(false); if (!isServer) { CmdRequestCharJson(); } } else { localPlayerTransform = transform; localPlayer = this; cameraRPG.instance.SetTarget(transform); #if UNITY_EDITOR ResetHealthAndArmor(); #else LoadPlayerData(); #endif statManager.OnStatsChanged += ConfigArmorHealthSliders; if (isServer) { playerName = gplayAuth.userNameCloud; myCharJson = CharacterSelection.selectedCharJson; RpcBroadcastCharJson(CharacterSelection.selectedCharJson); } else { if (gplayAuth.userNameCloud.Length > 0) { CmdSetName(gplayAuth.userNameCloud); } else { CmdSetName("Player" + Random.Range(0, 100)); } CmdSetCharJson(CharacterSelection.selectedCharJson); } } } [Command] public void CmdInvitePlayer(string otherPlayerName) { if (myPartyOwner == null || myPartyOwner.Length == 0) { FindPlayerByName(otherPlayerName).ShowInvite(playerName); } else { FindPlayerByName(otherPlayerName).ShowInvite(myPartyOwner); } } public void ShowInvite(string ownerName) { RpcInvitePlayer(ownerName); } [ClientRpc] void RpcInvitePlayer(string playerName) { if (!isLocalPlayer) { return; } invitePlayer.ShowInvite(playerName); } [Command] public void CmdAcceptInvite(string otherPlayerName) { myPartyOwner = otherPlayerName; Debug.Log("Invite accepted: " + myPartyOwner); } [Command] public void CmdLeaveParty() { myPartyOwner = null; } playerNetwork FindPlayerByName(string playerName) { playerNetwork[] players = FindObjectsOfType(); foreach (playerNetwork player in players) { if (player.playerName == playerName) { return player; } } return null; } void LoadCharFromJson(string json) { if (json.Length <= 0) { return; } character.FromJson(json, true); } public void SavePlayerData() { #if UNITY_EDITOR return; #endif Debug.Log("*** Save Method Got Called ! ***"); if (!isLoaded) { Debug.Log("*** Save Method Return ***"); return; } FirebaseFirestore db = FirebaseFirestore.DefaultInstance; //int playerCoin = int.Parse(coins.text); Dictionary saveValues = new Dictionary{ {"playerInventory" , JsonConvert.SerializeObject(inventory.inventoryManager.GetEntries())}, {"playerHealth" , health}, {"playerCoin", playerCoin}, {"killCount", enemyKillCount}, {"xp", XP}, {"completedQuest", completedQuests}, {"playerStats", statManager.PlayerStats}, {"characterJson", selectedCharacterJson}, }; DocumentReference docRef = db.Collection("PlayerData").Document(gplayAuth.userID); docRef.SetAsync(saveValues).ContinueWithOnMainThread(task => { if (task.IsCompleted) { Debug.Log("**** Save Completed Firestore ****"); } else { Debug.Log("**** Failed to save data to firestore ****"); } }); } public bool isLoaded = false; public void LoadPlayerData() { #if UNITY_EDITOR return; #endif Debug.Log("**** Data Load method got called ****"); FirebaseFirestore db = FirebaseFirestore.DefaultInstance; DocumentReference docRef = db.Collection("PlayerData").Document(gplayAuth.userID); docRef.GetSnapshotAsync().ContinueWithOnMainThread(task => { DocumentSnapshot snapshot = task.Result; if (snapshot.Exists) { Debug.Log("**** Found previous Data to load ****"); //load data // Dictionary dic = snapshot.ToDictionary(ServerTimestampBehavior.Estimate); // Debug.Log("Reading data"); // foreach(KeyValuePair item in dic){ // Debug.Log(item.Key + " : " +item.Value.ToString()); // } //saveNameTxt.text = snapshot.GetValue("playerName"); //load kills int _enemyKillCount = snapshot.GetValue("killCount"); SetEnemyKillCount(_enemyKillCount); //load XP XP = snapshot.GetValue("xp"); //Load coin int _playerCoin = snapshot.GetValue("playerCoin"); SetPlayerCoins(_playerCoin); // coinText.text = snapshot.GetValue("playerCoin").ToString(); //Load Health int savedHealth = snapshot.GetValue("playerHealth"); SetHealth(savedHealth); ResetHealthAndArmor(); healthBar.value = (savedHealth); armorBar.value = savedHealth; //load Inventory Dictionary inventoryGetData = JsonConvert.DeserializeObject>(snapshot.GetValue("playerInventory")); inventory.inventoryManager.SetInventory(inventoryGetData); completedQuests = snapshot.GetValue>("completedQuest"); //stats statManager.loadFromCloudSave(snapshot.GetValue>("playerStats")); isLoaded = true; } else { //show error previous data doesnt exists to load Debug.Log("**** No previous data to load ****"); isLoaded = true; } }); } [Command] void CmdSetName(string nameValue) { playerName = nameValue; } [Command(requiresAuthority = false)] void CmdRequestCharJson() { RpcBroadcastCharJson(myCharJson); } [Command] void CmdSetCharJson(string newValue) { myCharJson = newValue; RpcBroadcastCharJson(newValue); LoadCharFromJson(newValue); } [ClientRpc] void RpcBroadcastCharJson(string newValue) { LoadCharFromJson(newValue); } void OnDirectionChanged(Vector2 oldVal, Vector2 newVal) { character.SetDirection(newVal); } void OnAnimChanged(int oldVal, int newVal) { if (isLocalPlayer) { return; } character.AnimationManager.SetState((CharacterState)newVal); } rangeEnemyFinder rangeEnemyFind; enemyScript closestEnemy => rangeEnemyFind.targetEnemy; float attackTimer = 0; [HideInInspector] public PlayerAttack playerAttack; void Update() { if (isLocalPlayer) { if (attackTimer > 0) { attackTimer -= Time.deltaTime; } if (isServer) { SetAnimationData(character.Direction, character.Animator.GetInteger("State")); } else { CmdUpdateAnim(character.Direction, character.Animator.GetInteger("State")); } healthBar.value = (health); armorBar.value = health; txtEnemyKillCount.text = enemyKillCount.ToString(); coinText.text = playerCoin.ToString(); txtPlayerName.text = gplayAuth.userNameCloud; if (myPartyOwner != null && myPartyOwner.Length > 0) { invitePlayer.InParty(myPartyOwner); } else { invitePlayer.InParty(""); } } ShowXP(); ShowLevel(); } [Command] void CmdUpdateAnim(Vector2 direction, int animState) { SetAnimationData(direction, animState); } void SetAnimationData(Vector2 direction, int animState) { directionNetwork = direction; animIntNetwork = animState; if (!isLocalPlayer) { character.AnimationManager.SetState((CharacterState)animState); character.SetDirection(direction); } } void OnHealthChanged(int oldVal, int newVal) { if (!isLocalPlayer) { return; } // if (oldVal < newVal) { GameObject newObject = Instantiate(healthVfx, character.characterTransform()); newObject.transform.localPosition = Vector3.zero; newObject.transform.parent = transform; //StartCoroutine (Couroutine_autoDisableVFX(newObject)); vfxScript vfxSc = newObject.AddComponent(); } else if (oldVal > newVal) { //damage VFX GameObject newObject = Instantiate(damageVfx, character.characterTransform()); newObject.transform.localPosition = new Vector3(0, 5f, 0); newObject.transform.parent = transform; //StartCoroutine (Couroutine_autoDisableVFX(newObject)); vfxScript vfxSc = newObject.AddComponent(); // vfxSc.offsetVFX = new Vector3(0, 5f, 0); // vfxSc.target = character.characterTransform(); } SavePlayerData(); healthBar.value = (newVal); armorBar.value = newVal; } // IEnumerator Couroutine_autoDisableVFX(GameObject go){ // yield return new WaitForSecondsRealtime(5f); // Destroy(go); // } void onKillCountChange(int oldval, int newval) { if (!isLocalPlayer) { return; } // int prevLevel = GetLevelForKills(oldval); // int newLevel = GetLevelForKills(newval); SavePlayerData(); // if(newLevel > prevLevel){ // StartCoroutine(uiTxtDelay(25f)); // } // if(enemyKillCount == 5 ){ // //SavePlayerData(); // //QuestComplete(); // AddCoin(); // } } void OnXpChanged(int oldVal, int newVal) { if (!isLocalPlayer) { return; } int prevLevel = GetLevelByXp(oldVal); int newLevle = GetLevelByXp(newVal); if (newLevle > prevLevel) { int levelChange = newLevle - prevLevel; GameObject newObject = Instantiate(levelUpVfx, character.characterTransform()); newObject.transform.localPosition = Vector3.zero; newObject.transform.parent = transform; StartCoroutine(uiTxtDelay(25f, levelChange)); } } public void SetHealth(int newvalue) { if (isServer) { health = newvalue; healthBar.value = (newvalue); armorBar.value = newvalue; } else { CmdSetHealth(newvalue); } } public void SetEnemyKillCount(int newValue) { if (isServer) { enemyKillCount = newValue; } else { CmdSetEnemyKillCount(newValue); } } public void SetPlayerCoins(int newVal) { if (isServer) { playerCoin = newVal; } else { CmdSetPlayerCoin(newVal); } } [Command] void CmdSetPlayerCoin(int newVal) { playerCoin = newVal; } [Command] void CmdSetEnemyKillCount(int newValue) { enemyKillCount = newValue; } [Command] void CmdSetHealth(int newValue) { health = newValue; } public void TakeDamage(int attackDamage) { serverTakeDmg(attackDamage); // if(isLocalPlayer){ // takedmg(attackDamage); // }else if(isServer){ // RpcTakeDamage(attackDamage); // } } void serverTakeDmg(int damage) { health -= damage; if (health <= 0) { RpcDeath(); death(); } } float xpTimer = 0; public void ShowXP() { if (xpTimer > 0) { xpTimer -= Time.deltaTime; return; } xpTimer = 1; xpText.text = (Mathf.RoundToInt(XP / 100f) * 100f).ToString(); xpSlider.value = XpSliderVal; for (int i = 0; i < 10; i++) { float val = (float)i / 10f; xpSliderSlots[i].SetActive(xpSlider.value > val); } } public void ShowLevel() { lvlText.text = lvl.ToString(); } public void OnEnemyKilled(int enemyLevel) { //disable take damage and disable healthbar going backwards int prevValue = lvl; // SavePlayerData(); enemyKillCount++; //XP += (int)(enemyScript.XP_GAIN * (enemyLevel/2f)); XP += enemyScript.XP_GAIN_Base + Mathf.FloorToInt(enemyScript.XP_GAIN * (enemyLevel - 1)); } IEnumerator uiTxtDelay(float delayTime, int levelChange) { //enable xpEnableTxt.gameObject.SetActive(true); //int attackDamageChange= 5 * levelChange; //attackDmgEnableTxt.gameObject.SetActive(true); //attackDmgEnableTxt.text = "Attack Damage + " + attackDamageChange; yield return new WaitForSecondsRealtime(delayTime); //disable xpEnableTxt.gameObject.SetActive(false); //attackDmgEnableTxt.gameObject.SetActive(false); } // public void QuestComplete(){ // //task completion logic // // Strikethrough the text // //questText.text = "Kill 5 Enemies to claim 100Golds Completed"; // Debug.Log("First quest completed"); // } public void AddCoin() { playerCoin += 100; coinText.text = playerCoin.ToString(); } [ClientRpc] public void RpcDeath() { death(); } public bool isDead = false; public void death() { Debug.Log("Death called"); character.AnimationManager.Die(); isDead = true; StartCoroutine(CouroutineWaitDeath()); // throw new System.Exception(); } IEnumerator CouroutineWaitDeath() { yield return new WaitForSecondsRealtime(3); isDead = false; playerRespawn(); } public void OnAttack() { if (attackTimer > 0) { return; } //characterMan.SetActiveWeapon(78); attackTimer = ATTACK_COOLDOWN; if (isLocalPlayer) { PlayAttackAnim(); playerAttack.Attack(false); if (isServer) { RpcPlayAttackAnim(); } else { CmdPlayAttackAnim(); } } } [SerializeField] public float arrowSpeed = 2.0f; [SerializeField] public float arrowShootOffset = 1; [SerializeField] public float arrowShootHeightOffset = 1; public void OnRangeAttack() { if (attackTimer > 0) { return; } if (closestEnemy == null) { return; } attackTimer = ATTACK_COOLDOWN; characterMan.EquipBow(); PlayAttackAnim(); StartCoroutine(ArrowShootDelay()); // arrowRange.transform.position = startingPosition; //TODO: Deal Damage once it hits enemy } public float arrowDelay; IEnumerator ArrowShootDelay() { yield return new WaitForSeconds(arrowDelay); Vector3 startingPosition = transform.position; Vector3 destination = closestEnemy.transform.position; //TODO: Move attack projectile from startingPosition to destination Vector3 direction = (destination - startingPosition).normalized; startingPosition += (direction * arrowShootOffset); startingPosition += (Vector3.up * arrowShootHeightOffset); destination += (Vector3.up * arrowShootHeightOffset); // Quaternion rotation = Quaternion.LookRotation(direction); float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; Quaternion rotation = Quaternion.Euler(0, 0, angle - 90f); GameObject newArrow = Instantiate(arrowRange, transform.position + (direction * arrowShootOffset), rotation); StartCoroutine(moveArrow(newArrow.transform, startingPosition, destination, closestEnemy)); } public int RangeDmg = 10; IEnumerator moveArrow(Transform arrow, Vector3 start, Vector3 destination, enemyScript target) { float dist = Vector3.Distance(start, destination); float duration = dist / arrowSpeed; float timer = 0f; while (timer < duration) { arrow.position = Vector3.Lerp(start, destination, timer / duration); timer += Time.deltaTime; yield return null; } target.TakeDamage(RangeDmg, netId); Destroy(arrow.gameObject); } public int MagicAttackWeaponIndex = 52; public void OnMagicAttack() { if (attackTimer > 0) { return; } attackTimer = ATTACK_COOLDOWN; //characterMan.SetActiveWeapon(MagicAttackWeaponIndex); if (isLocalPlayer) { PlayAttackAnim(); //? playerAttack.MagicalAttack(); playerAttack.Attack(true); if (isServer) { RpcPlayAttackAnim(); } else { CmdPlayAttackAnim(); } } } [Command] void CmdPlayAttackAnim() { PlayAttackAnim(); RpcPlayAttackAnim(); } [ClientRpc] void RpcPlayAttackAnim() { if (isLocalPlayer) { return; } PlayAttackAnim(); } void PlayAttackAnim() { switch (character.WeaponType) { case WeaponType.Melee1H: case WeaponType.Paired: character.AnimationManager.Slash(twoHanded: false); break; case WeaponType.Melee2H: character.AnimationManager.Slash(twoHanded: true); break; case WeaponType.Bow: character.AnimationManager.ShotBow(); break; } } public void playerRespawn() { Debug.Log("Respawning"); // healthBar.SetMaxHealth(statManager.GetEffectiveValue("health")); ResetHealthAndArmor(); healthBar.value = health; character.AnimationManager.SetState(CharacterState.Idle); Transform newSpawnLocationPlayer = GameManager.instance.spawnPointsPlayer.GetChild(Random.Range(0, GameManager.instance.spawnPointsPlayer.childCount)); transform.position = newSpawnLocationPlayer.position; } void ResetHealthAndArmor() { healthBar.maxValue = statManager.GetEffectiveValue("health"); health = statManager.GetEffectiveValue("health") + statManager.GetEffectiveValue("defence"); // Debug.Log($"Setting armor bar, maxVal:{health}, minVal:{healthBar.maxValue}, val:{health}"); armorBar.maxValue = health; armorBar.minValue = healthBar.maxValue; armorBar.value = health; } void ConfigArmorHealthSliders() { healthBar.maxValue = statManager.GetEffectiveValue("health"); float maxHealth = statManager.GetEffectiveValue("health") + statManager.GetEffectiveValue("defence"); // Debug.Log($"Setting armor bar, maxVal:{health}, minVal:{healthBar.maxValue}, val:{health}"); armorBar.maxValue = maxHealth; armorBar.minValue = healthBar.maxValue; armorBar.value = health; healthBar.value = health; } //Pickup public void PickupObject(pickup item) { if (!isServer) { Debug.LogError("Cant call command on client, 403"); return; } if (isLocalPlayer) { pickupObject(item.lootData.type); } else { RpcPickupObject(item.lootData.type); } } [ClientRpc] void RpcPickupObject(string type) { if (isLocalPlayer) { pickupObject(type); } } void pickupObject(string type) { inventory.AddItem(type); } public void DropPickup(string type) { if (isServer) { GameManager.instance.SpawnPickup(type, transform.position + new Vector3(0.85f, 0.6f)); } else { CmdDropPickup(type); } } [Command] void CmdDropPickup(string type) { GameManager.instance.SpawnPickup(type, transform.position + new Vector3(4, 0)); } int magicalDmg = 0; public void MagicalAttack(Vector3 direction, float magicalProjectileSpawnOffset, int dmg) { if (isServer) { magicalAttack(direction, magicalProjectileSpawnOffset, dmg); } else { CmdMagicalAttack(direction, magicalProjectileSpawnOffset, dmg); } } [Command] void CmdMagicalAttack(Vector3 direction, float magicalProjectileSpawnOffset, int dmg) { magicalAttack(direction, magicalProjectileSpawnOffset, dmg); } void magicalAttack(Vector3 direction, float magicalProjectileSpawnOffset, int dmg) { magicalDmg = dmg; GameObject projectileSpawned = Instantiate(projectile, transform.position + (direction * magicalProjectileSpawnOffset), Quaternion.identity); RangeProjectile _projectile = projectileSpawned.GetComponent(); _projectile.direction = direction; _projectile.OnHit.AddListener(OnMagicalHit); _projectile.shooterId = netId; NetworkServer.Spawn(projectileSpawned); } void OnMagicalHit(enemyScript victim) { //magical damage with intelligance stat ? //int damageamount = magicalDmg + (lvl * 5); int damageamount = magicalDmg + (statManager.GetEffectiveValue("intelligence") * 2); Debug.Log("magic damage amount " + damageamount); victim.TakeMagicalDamage(damageamount, netId); } public void GoBackMenu() { startClient.instance.networkManager.StopClient(); SceneManager.LoadScene("GameLogin"); #if UNITY_EDITOR || UNITY_SERVER || UNITY_STANDALONE_WIN #else PlayGamesPlatform.Instance.SignOut(); Firebase.Auth.FirebaseAuth.DefaultInstance.SignOut(); #endif } }