using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; namespace Kryz.CharacterStats.Examples { public class PlayerCharacterStats : CharacterStats { [Header("Listeners:")] [SerializeField] private GameEventListener_Float experienceOnNPCDeath; [SerializeField] private GameEventListener_JobInstance onJobCompleted; [Header("Components:")] [SerializeField] Inventory inventory; [SerializeField] EquipmentPanel equipmentPanel; [SerializeField] StatPanel statPanel; [HideInInspector] public RiftPlayer player; public Level level; public ReputationLevel reputationLevel; Health health; Mana mana; SpiritPower spiritPower; [Header("Runtime Data:")] public List AllocatedStatPoints = new List(); public int AvailablePointsToAllocate; CharacterData characterData = new CharacterData(); PlayerAccountData accountData = new PlayerAccountData(); List statsCollection = new List(); protected override void Awake() { player = GetComponentInParent(); health = player.GetComponent(); mana = player.GetComponent(); spiritPower = player.GetComponent(); level = new Level(); base.Awake(); accountData = PlayerDataHandler.Instance.LoadPlayerAccountData(PlayerDataHandler.Instance.currentPlayerName.Value); characterData = PlayerDataHandler.Instance.LoadCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value); if (characterData != null) { Debug.Log("Success Loading CharacterData"); level = new Level(characterData.currentLevel, characterData.currentExperience); reputationLevel = new ReputationLevel(accountData.currentReputationLevel, accountData.currentReputationExperience); int totalAllocatedPoints = 0; for (int i = 0; i < primaryStatsDictionary.Keys.Count; i++) { AllocatedStatPoints.Add(characterData.allocatedStatPoints[i]); totalAllocatedPoints += characterData.allocatedStatPoints[i]; } int totalPointsFromLevels = level.currentLevel * GameConstants.CharacterStatsBalancing.StatPointsPerLevel; int totalPointsFromRep = reputationLevel.currentLevel * GameConstants.CharacterStatsBalancing.StatPointsPerReputationLevel; if (totalAllocatedPoints + characterData.availablePointsToAllocate < totalPointsFromLevels + totalPointsFromRep) { characterData.availablePointsToAllocate = totalPointsFromLevels + totalPointsFromRep - totalAllocatedPoints; } AvailablePointsToAllocate = characterData.availablePointsToAllocate; UpdateAllocatedStats(); UpdateStatsBasedOnLevel(); } else { characterData = new CharacterData(); reputationLevel = new ReputationLevel(accountData.currentReputationLevel, accountData.currentReputationExperience); for (int i = 0; i < primaryStatsDictionary.Keys.Count; i++) { AllocatedStatPoints.Add(0); } AvailablePointsToAllocate = GameConstants.CharacterStatsBalancing.StatPointsPerLevel; AvailablePointsToAllocate += accountData.currentReputationLevel * GameConstants.CharacterStatsBalancing.StatPointsPerReputationLevel; characterData.playerOwnerID = PlayerDataHandler.Instance.currentPlayerName.Value; characterData.currentLevel = level.currentLevel; characterData.currentExperience = level.GetCurrentExperience(); characterData.availablePointsToAllocate = AvailablePointsToAllocate; for (int i = 0; i < AllocatedStatPoints.Count; i++) { characterData.allocatedStatPoints[i] = AllocatedStatPoints[i]; } PlayerDataHandler.Instance.SaveCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, characterData); } if (statPanel == null) statPanel = FindObjectOfType(); statPanel.SetPlayerStats(this); statPanel.SetStats(primaryStatsDictionary); statPanel.SetSecondaryStats(secondaryStatsDictionary); //TODO: statPanel set misc stats (area, cdr, movespeed, rep, gold statPanel.UpdateStatValues(); statPanel.SetCharacterInfo(PlayerDataHandler.Instance.currentCharacterName.Value, level.currentLevel.ToString(), reputationLevel.currentLevel.ToString()); statPanel.ToggleAllocateButtonsInteractable(AvailablePointsToAllocate > 0); onAllStatsUpdated.AddListener(statPanel.UpdateStatValues); onUpdateStatValues.Invoke(); if (inventory == null) inventory = FindObjectOfType(); inventory.OnItemRightClickedEvent.AddListener(EquipFromInventory); if (equipmentPanel == null) equipmentPanel = FindObjectOfType(); equipmentPanel.OnItemRightClickedEvent.AddListener(UnequipFromEquipPanel); RefreshCurrentEquipmentStats(); level.OnLevelUpEvent.AddListener(OnLevelUp); level.OnExperienceChanged.AddListener(OnExperienceChanged); reputationLevel.OnLevelUpEvent.AddListener(OnReputationLevelUp); reputationLevel.OnExperienceChanged.AddListener(OnReputationExperienceChanged); experienceOnNPCDeath.Response.AddListener(level.GainExperience); onJobCompleted.Response.AddListener(job => level.GainExperience(job.experienceReward)); onJobCompleted.Response.AddListener(job => reputationLevel.GainExperience(job.reputationReward)); statPanel.UpdateStatValues(); } private void Start() { onUpdateStatValues.Invoke(); } private void OnExperienceChanged() { characterData.currentLevel = level.currentLevel; characterData.currentExperience = level.GetCurrentExperience(); PlayerDataHandler.Instance.SaveCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, characterData); } private void OnReputationExperienceChanged() { accountData.currentReputationLevel = reputationLevel.currentLevel; accountData.currentReputationExperience = reputationLevel.GetCurrentExperience(); PlayerDataHandler.Instance.SavePlayerAccountData(PlayerDataHandler.Instance.currentPlayerName.Value, accountData); } private void OnReputationLevelUp() { AvailablePointsToAllocate += GameConstants.CharacterStatsBalancing.StatPointsPerReputationLevel; characterData.availablePointsToAllocate = AvailablePointsToAllocate; statPanel.ToggleAllocateButtonsInteractable(AvailablePointsToAllocate > 0); } private void OnLevelUp() { statPanel.UpdateLevelInfo(level.currentLevel.ToString(), reputationLevel.currentLevel.ToString()); UpdateStatsBasedOnLevel(); AvailablePointsToAllocate += GameConstants.CharacterStatsBalancing.StatPointsPerLevel; characterData.availablePointsToAllocate = AvailablePointsToAllocate; statPanel.ToggleAllocateButtonsInteractable(AvailablePointsToAllocate > 0); health.ChangeValue(health.GetMaxValue() * 0.2f); mana.ChangeValue(mana.GetMaxValue() * 0.2f); } private void EquipFromInventory(ItemInstance item) { if (item is EquippableItemInstance) { Equip((EquippableItemInstance)item); } } private void UnequipFromEquipPanel(ItemInstance item) { if (item is EquippableItemInstance) { Unequip((EquippableItemInstance)item); } } public void Equip(EquippableItemInstance item) { // First, check if we have enough inventory space for displaced items if (!CanEquipItem(item)) { // Could show a message to player here: "Not enough inventory space" return; } if (inventory.RemoveItem(item)) { ItemTooltip.Instance.HideTooltip(); EquippedItemTooltip.Instance.HideTooltip(); EquippableItemInstance previousItem; EquippableItemInstance secondPreviousItem; if (equipmentPanel.AddItem(item, out previousItem, out secondPreviousItem)) { // Handle first previous item if (previousItem != null && !string.IsNullOrEmpty(previousItem.ItemName)) { inventory.AddItem(previousItem); previousItem.Unequip(this); } // Handle second previous item (for two-handed weapon replacement) if (secondPreviousItem != null && !string.IsNullOrEmpty(secondPreviousItem.ItemName)) { inventory.AddItem(secondPreviousItem); secondPreviousItem.Unequip(this); } // Equip the new item item.Equip(this); statPanel.UpdateStatValues(); onUpdateStatValues.Invoke(); } else { inventory.AddItem(item); } } } // Simplified version using the EquipmentPanel helper method private bool CanEquipItem(EquippableItemInstance item) { int itemsToDisplace = equipmentPanel.GetDisplacedItemCount(item); // Calculate available inventory slots // We get +1 slot from removing the item we're equipping int availableSlots = inventory.AvailableSlots() + 1; return availableSlots >= itemsToDisplace; } public void Unequip(EquippableItemInstance item) { if (!inventory.IsFull() && equipmentPanel.RemoveItem(item)) { item.Unequip(this); statPanel.UpdateStatValues(); inventory.AddItem(item); onUpdateStatValues.Invoke(); } } public void UpdateAllocatedStats() { Cunning.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource); Flow.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource); Presence.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource); Cunning.AddModifier(new StatModifier(AllocatedStatPoints[0], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource)); Flow.AddModifier(new StatModifier(AllocatedStatPoints[1], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource)); Presence.AddModifier(new StatModifier(AllocatedStatPoints[2], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource)); characterData.availablePointsToAllocate = AvailablePointsToAllocate; for (int i = 0; i < AllocatedStatPoints.Count; i++) { characterData.allocatedStatPoints[i] = AllocatedStatPoints[i]; } PlayerDataHandler.Instance.SaveCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, characterData); onUpdateStatValues.Invoke(); } public void UpdateStatsBasedOnLevel() { GetStat("maxhealth").RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource); //Debug.Log("MAX HEALTH BASE VALUE: " + MaxHealth.BaseValue); GetStat("maxhealth").AddModifier(new StatModifier(GetStat("maxhealth").BaseValue * (level.currentLevel - 1) * GameConstants.CharacterStatsBalancing.BaseMaxHealthGrowthPerLevel, StatModType.Flat, GameConstants.ObjectSources.LevelSource)); onUpdateStatValues.Invoke(); } EquipmentSlot[] slots; public void RefreshCurrentEquipmentStats() { slots = equipmentPanel.GetCurrentSlots(); for (int i = 0; i < slots.Length; i++) { if (slots[i].Item == null) continue; for (int j = 0; j < statsCollection.Count; j++) { if (statsCollection[j].HasModifiersFromSource(slots[i].Item)) { break; } } ((EquippableItemInstance)slots[i].Item).Equip(this); statPanel.UpdateStatValues(); onUpdateStatValues.Invoke(); } } } }