using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using Invector; using Invector.vCharacterController; using UnityEngine.Events; using PixelCrushers.DialogueSystem; using PixelCrushers.Wrappers; using Sirenix.OdinInspector; using PixelCrushers.QuestMachine; using QuestState = PixelCrushers.QuestMachine.QuestState; using Random = UnityEngine.Random; using UnityEngine.UI; using Invector.vCharacterController.vActions; using Invector.vMelee; using static PixelCrushers.DialogueSystem.DialogueSystemEvents; namespace Beyond { [RequireComponent(typeof(AudioSource))] public class Player : MonoBehaviour { [SerializeField] private List m_playerAttributes = new List(); public static readonly int[] BrightnessPoints_Th = { 30, 20, 0 }; public List Attributes => m_playerAttributes; public GameObject audioSource; public AudioClip[] m_onHitClips, m_noFaithClips, m_cantDoThatYetClips, m_BrightnessLostClips, m_BrightnessGainedClips, m_deathClips; public AudioClip[] consumeDefaultSounds, consumeFaithSounds, consumeGemstoneSounds, equipWeaponSounds, equipConsumablesSounds, equipPowerSounds, unequipWeaponSounds, unequipConsumablesSounds, unequipPowerSounds, changePageSounds, changeCategorySounds, openMenuSounds, acceptGuiltSounds, declineGuiltSounds, fullyChargedSounds; private List weaponTypes = new List { bItemType.Swords, bItemType.Axes }; private List consumablesTypes = new List { bItemType.Consumable, bItemType.ConsumablesFaith }; public Image interactionImage; private Sprite defaultInteractionImage; public Sprite dialogueInteractionImage; protected AudioSource m_audioSource; public AudioSource AudioSource => m_audioSource; private bThirdPersonInput m_vInputs; private bThirdPersonController m_vController; private vMeleeManager m_meleeManager; private Respawner m_Respawner; private bool m_cutScenePlaying = false; private QuestJournal m_questJournal; private PlayerConfessionController m_playerConfessionController; public QuestJournal QuestJournal => m_questJournal; public PlayerConfessionController PlayerConfessionController => m_playerConfessionController; public bThirdPersonInput PlayerInput => m_vInputs; public bThirdPersonController ThirdPersonController => m_vController; public float slowMoOnHtScale = 0.1f; public float slowMoOnHitTime = 0.1f; public vMeleeManager MeleeManager => m_meleeManager; private static Player s_instance; public static Player Instance => s_instance; public bItemManager ItemManager { private set; get; } public WeaponTrail ActiveWeaponTrail { set; get; } private PlayerAttribute faithAttribute; private PlayerAttribute brightnessAttribute; private PlayerAttribute maturityAttribute; private MagicAttacks m_magicAttacks; private bMeleeCombatInput m_meleeCombatInput; public MagicAttacks Magic => m_magicAttacks; public bMeleeCombatInput MeleeCombatInput => m_meleeCombatInput; // --- TRINKET SYSTEM INTEGRATION START --- // Initialize default values to 1f to prevent divide-by-zero or zero-stat issues on startup private TrinketManager.TrinketStats m_trinketStats = new TrinketManager.TrinketStats { healthMult = 1f, defenseMult = 1f, faithMult = 1f, damageMult = 1f, speedMult = 1f, faithRegenMult = 1f, attackSpeedMult = 1f, thornDamageMult = 1f, staminaMult = 1f, soulfireDamageMult = 1f }; public TrinketManager.TrinketStats CurrentTrinketStats => m_trinketStats; public void UpdateTrinketStats(TrinketManager.TrinketStats newStats) { m_trinketStats = newStats; // 1. Recalculate Base Stats (Health/Faith/Stamina/Natural Regen) // This sets m_vController.healthRecovery to the base natural value (from Maturity/Brightness) UpdatePlayerStatistics(); // 2. Apply Speed & Animation Speed if (m_vController) { m_vController.speedMultiplier = m_trinketStats.speedMult; if (m_vController.animator) m_vController.animator.SetFloat("AttackSpeed", m_trinketStats.attackSpeedMult); // --- HEALTH REGEN LOGIC START --- // Start with the base natural rate we just calculated in UpdatePlayerStatistics float totalRegenRate = m_vController.healthRecovery; float totalRegenCap = 1f; // Default 100% cap // Add Passive Trinket Regen (e.g. "Growth" effect: Regen up to 50%) if (m_trinketStats.effectGrowth) { // Example: +2 HP/sec up to 50% totalRegenRate += 2f; totalRegenCap = 0.5f; } // If you cast to bThirdPersonController, you can set the Cap if (m_vController is bThirdPersonController bController) { bController.healthRecoveryCap = totalRegenCap; } // Update the final rate on the controller m_vController.SetHealthRecovery(totalRegenRate); // --- HEALTH REGEN LOGIC END --- } // 3. Apply Damage Multipliers if (m_meleeManager) { // Start with the base multiplier from items float totalDamageMult = m_trinketStats.damageMult; // "The Darkening": Bonus dmg if (m_trinketStats.effectDarkening) { totalDamageMult += 0.10f; } // "Determination": Combo finish +5% damage // (Global application for now) if (m_trinketStats.effectDetermination) { totalDamageMult += 0.05f; } // Send final value to your modified vMeleeManager // NOTE: Requires the SetGlobalDamageMultiplier method added to vMeleeManager m_meleeManager.SetGlobalDamageMultiplier(totalDamageMult); } } // --- TRINKET SYSTEM INTEGRATION END --- public PlayerAttribute MaturityAttribute { get { SetMaturityAttributeIfNull(); return maturityAttribute; } } public PlayerAttribute BrightnessAttribute { get { SetBrightnessAttributeIfNull(); return brightnessAttribute; } } public PlayerAttribute FaithAttribute { get { SetFaithAttributeIfNull(); return faithAttribute; } } private void SetMaturityAttributeIfNull() { if (maturityAttribute == null) maturityAttribute = GetAttribute("Maturity"); } private void SetBrightnessAttributeIfNull() { if (brightnessAttribute == null) brightnessAttribute = GetAttribute("BrightnessPoints"); } private void SetFaithAttributeIfNull() { if (maturityAttribute == null) faithAttribute = GetAttribute("Faith"); } [SerializeField] public MenuScroll menuScroll; public float sceneDependantFaithRegenMultiplier = 1f, faithRegenMultiplier = 1f; private float healthBaseMaxValue = 200; private float healthBaseRegenValue = 0f; [SerializeField] private float staminaBaseMaxValue = 200; [SerializeField] private float staminaBaseRegenValue = 1.2f; private float faithBaseMaxValue = 100f; private System.Action onMenuScrollClosed; // Updated Action signature to pass 3 floats for UI (Health, Faith, Stamina) public System.Action onStatsUpdated; private UnityAction onDialogueEnded; private bLockOn m_lockOn; public bLockOn LockOn => m_lockOn; private vLadderAction ladderAction; public float CurrentHealth => m_vController.currentHealth; public float MaxHealth => m_vController.MaxHealth; public AutoTargetting AutoTarget => m_autoTargetting; private AutoTargetting m_autoTargetting; private void Awake() { if (s_instance == null) s_instance = this; else { Debug.LogError($"Player instance already exists! Destroying...({gameObject.name})"); Destroy(gameObject); } m_lockOn = GetComponent(); ladderAction = GetComponent(); m_audioSource = GetComponent(); m_vInputs = GetComponent(); m_vController = GetComponent(); ItemManager = GetComponent(); m_questJournal = GetComponent(); m_Respawner = GetComponent(); m_playerConfessionController = GetComponent(); m_meleeManager = GetComponent(); m_magicAttacks = GetComponent(); m_autoTargetting = GetComponent(); m_meleeCombatInput = GetComponent(); } private void OnEnable() { HideUI.SetActive += OnCustcene; if (m_vInputs) m_vInputs.forceWalking = false; } protected void Start() { InitAttributesValues(); InitAttributListeners(); RegisterLuaFunctions(); DialogueManager.instance.conversationStarted += OnConversationStarted; DialogueManager.instance.conversationEnded += OnConversationEnded; m_Respawner.m_onRespawned.AddListener(ResetInputs); m_vController.onDead.AddListener(ResetAnimator); if (m_meleeManager) { m_meleeManager.onDamageHit.AddListener(OnDamageHit); } } private void OnDamageHit(vHitInfo arg0) { // Slow motion logic if (slowMoOnHtScale < 1f - float.Epsilon) { TimeController.Instance.Reset(); } // 1. Health Vampirism (Vitality) if (m_trinketStats.effectHealthVampirism) { // Heal 2% of Max Health per hit, min 1 HP int healAmount = Mathf.Max(1, (int)(MaxHealth * 0.02f)); m_vController.ChangeHealth(healAmount); } // 2. Faith Vampirism (Trust) if (m_trinketStats.effectFaithVampirism) { // Add 1 Faith point per hit UpdateFaithCurrentValue(1); } } private void OnConversationStarted(Transform transform) { PlayerInput.SetLockAllInput(true); } private void OnConversationEnded(Transform transform) { PlayerInput.SetLockAllInput(false); } private void OnDisable() { HideUI.SetActive -= OnCustcene; } protected void OnDestroy() { RemoveAttributeListeners(); UnregisterLuaFunctions(); if (menuScroll != null) menuScroll.OnClosed -= onMenuScrollClosed; if (DialogueManager.instance != null) { DialogueManager.instance.conversationStarted -= OnConversationStarted; DialogueManager.instance.conversationEnded -= OnConversationEnded; } m_Respawner.m_onRespawned.RemoveListener(ResetInputs); m_Respawner.m_onRespawnedStart.RemoveListener(ResetAnimator); } protected void Update() { float faith = faithAttribute.AttributeCurrentValue; faith += Time.deltaTime * faithRegenMultiplier; faithAttribute.SetValue(faith); } public void ResetInputs() { bThirdPersonController controller = (bThirdPersonController)m_vController; controller.triggerDieBehaviour = false; ladderAction = GetComponent(); ladderAction.ResetPlayerSettings(); } public void ResetAnimator(GameObject gameObject) { ResetAnimator(); } public void ResetAnimator() { bThirdPersonController controller = (bThirdPersonController)m_vController; controller.animator.SetInteger(vAnimatorParameters.ActionState, 0); controller.RemoveAnimatorTags(); } // ... [Audio Play Methods] ... public void PlayNoFaithClip() { PlayRandomSound(m_noFaithClips); } private void PlayRandomSound(AudioClip[] sounds) { if (Time.timeSinceLevelLoad < 1f) return; int soundsCount = sounds.Length; if (audioSource != null && soundsCount > 0) { AudioClip clip = sounds[Random.Range(0, soundsCount)]; GameObject audioObject = Instantiate(audioSource, transform.position, transform.rotation) as GameObject; audioObject.GetComponent().PlayOneShot(clip); } } public void PlayICantDoThatYet() { PlayRandomSound(m_cantDoThatYetClips); } public void PlayBrightnessLost() { PlayRandomSound(m_BrightnessLostClips); } public void PlayBrightnessGained() { PlayRandomSound(m_BrightnessGainedClips); } public void PlayMaturityGained() { PlayRandomSound(m_BrightnessGainedClips); } public void PlayConsumeSound(bItem consumedItem) { if (consumedItem.type == bItemType.ConsumablesFaith) PlayRandomSound(consumeFaithSounds); else if (consumedItem.type == bItemType.Gemstones) PlayRandomSound(consumeGemstoneSounds); else if (consumedItem.type == bItemType.PowerScroll) { } else PlayRandomSound(consumeDefaultSounds); } public void PlayFullyChargedSound() { PlayRandomSound(fullyChargedSounds); } public void PlayEquipSound(bEquipArea equipArea, bItem item) { if (consumablesTypes.Contains(item.type)) PlayRandomSound(equipConsumablesSounds); else if (weaponTypes.Contains(item.type)) PlayRandomSound(equipWeaponSounds); else PlayRandomSound(equipPowerSounds); } public void PlayUnequipSound(bEquipArea equipArea, bItem item) { if (consumablesTypes.Contains(item.type)) PlayRandomSound(unequipConsumablesSounds); else if (weaponTypes.Contains(item.type)) PlayRandomSound(unequipWeaponSounds); else PlayRandomSound(unequipPowerSounds); } public void PlayCategoryChangeSound() { PlayRandomSound(changeCategorySounds); } public void PlayChangePageSound() { PlayRandomSound(changePageSounds); } public void PlayOpenMenuSound() { PlayRandomSound(openMenuSounds); } public void PlayAcceptGuiltSound() { PlayRandomSound(acceptGuiltSounds); } public void PlayDeclineGuiltSound() { PlayRandomSound(declineGuiltSounds); } public void PlayOnHitSound() { PlayRandomSound(m_onHitClips); } public void PlayOnDeathSound() { PlayRandomSound(m_deathClips); } private void OnCustcene(bool b) { m_audioSource.Stop(); m_audioSource.clip = null; m_cutScenePlaying = !b; m_vController.isImmortal = !b; } public void OnReceivedDamage(vDamage damage) { if (m_cutScenePlaying) return; // --- 1. DEFENSE CALCULATION --- // Example: 0.9 defenseMult = 90% damage taken (10% reduction) if (Mathf.Abs(m_trinketStats.defenseMult - 1f) > float.Epsilon) { damage.damageValue = (int)(damage.damageValue * m_trinketStats.defenseMult); } // --- 2. THORN DAMAGE (Reflect Damage) --- if (m_trinketStats.thornDamageMult > 1f && damage.sender != null) { // Calculate reflect amount (Base damage * (Mult - 1)) int thornVal = (int)(damage.damageValue * (m_trinketStats.thornDamageMult - 1f)); if (thornVal > 0) { vDamage reflectDmg = new vDamage(thornVal); reflectDmg.sender = transform; reflectDmg.damageType = "Thorns"; reflectDmg.reaction_id = -1; // No flinch // Uses Invector extension method to apply damage to sender damage.sender.gameObject.ApplyDamage(reflectDmg); } } // ---------------------------------------- #if UNITY_IOS && !UNITY_EDITOR HapticEngine.ImpactFeedbackHeavy(); #endif if (m_onHitClips != null && m_onHitClips.Length > 0) PlayOnHitSound(); } public void OnDead(GameObject gameObject) { if (m_cutScenePlaying) return; #if UNITY_IOS && !UNITY_EDITOR HapticEngine.ImpactFeedbackHeavy(); #endif if (m_deathClips != null && m_deathClips.Length > 0) PlayOnDeathSound(); } public void OnCheckpoint() { if (m_Respawner) m_Respawner.SaveRespawnPoint(); } // ... [Quest and Attribute Methods] ... public List GetAllGuilts() { if (!m_questJournal) { Debug.LogError("There is no Players Journal component on player game object"); return null; } List guilts = new List(); for (int i = 0; i < m_questJournal.questList.Count; i++) { if (m_questJournal.questList[i].isTrackable == false) { guilts.Add(m_questJournal.questList[i]); } } return guilts; } [Button] public void Debug_DisplayAllGuilts() { var guilts = GetAllGuilts(); guilts.ForEach(x => Debug.LogError(x.title)); } public PlayerAttribute GetAttribute(string name) { for (int i = 0; i < m_playerAttributes.Count; i++) { if (m_playerAttributes[i].AttributeName == name) return m_playerAttributes[i]; } Debug.LogError("There is no player attribute such " + name); return null; } public void SetAttribute(string name, int value) { PlayerAttribute playerAttribute = GetAttribute(name); if (playerAttribute == null) return; playerAttribute.SetValue(value); } public void SetAttribute(PlayerAttribute attribute) { PlayerAttribute playerAttribute = GetAttribute(attribute.AttributeName); if (playerAttribute == null) return; playerAttribute.SetValue(attribute); } public void ClearAttributesValue() { m_playerAttributes.ForEach(x => x.ClearValue()); } private void InitAttributesValues() { m_playerAttributes.ForEach(x => x.Init()); } private void InitAttributListeners() { brightnessAttribute = GetAttribute("BrightnessPoints"); if (brightnessAttribute != null) { brightnessAttribute.OnValueChanged.AddListener(OnBrightnessPointsValueChanged); brightnessAttribute.OnValueChanged.AddListener(UpdatePlayerStatisticsOnBrightnessChange); } else Debug.LogError("Couldnt get Brightness attribute "); faithAttribute = GetAttribute("Faith"); if (faithAttribute == null) Debug.LogError("Couldnt get Faith attribute "); maturityAttribute = GetAttribute("Maturity"); if (maturityAttribute != null) { maturityAttribute.OnValueChanged.AddListener(UpdatePlayerStatisticsOnMaturityChange); maturityAttribute.OnValueChanged.AddListener(OnMaturityPointsValueChanged); } else Debug.LogError("Couldnt get Maturity attribute "); } private void RemoveAttributeListeners() { maturityAttribute.OnValueChanged.RemoveListener(UpdatePlayerStatisticsOnMaturityChange); maturityAttribute.OnValueChanged.RemoveListener(OnMaturityPointsValueChanged); brightnessAttribute.OnValueChanged.RemoveListener(UpdatePlayerStatisticsOnBrightnessChange); } private void RegisterLuaFunctions() { Lua.RegisterFunction("GetBrightness", this, SymbolExtensions.GetMethodInfo(() => GetBrightness())); Lua.RegisterFunction("SetBrightness", this, SymbolExtensions.GetMethodInfo(() => SetBrightness((double)0))); Lua.RegisterFunction("SetMaturity", this, SymbolExtensions.GetMethodInfo(() => SetMaturity((double)0))); Lua.RegisterFunction("GetMaturity", this, SymbolExtensions.GetMethodInfo(() => GetMaturity())); } private void UnregisterLuaFunctions() { Lua.UnregisterFunction("GetBrightness"); Lua.UnregisterFunction("SetBrightness"); Lua.UnregisterFunction("SetMaturity"); Lua.UnregisterFunction("GetMaturity"); } public void UpdatePlayerStatistics() { float maturityMupltiplier = (float)(1 + (float)(0.5f * maturityAttribute.AttributeCurrentValue / maturityAttribute.AttributeMaxValue)); float maxBrightness = brightnessAttribute.AttributeMaxValue; float halfB = maxBrightness / 2; float brightnessMultiplier = 1 + (brightnessAttribute.AttributeCurrentValue - halfB) / halfB; float finalMultiplier = maturityMupltiplier * brightnessMultiplier; SetPlayerStatisticsBasedOn(finalMultiplier); } public void UpdatePlayerStatisticsOnBrightnessChange(float val = 0, float prevVal = 0) { float maturityMupltiplier = (float)(1 + (float)(0.5f * maturityAttribute.AttributeCurrentValue / maturityAttribute.AttributeMaxValue)); float maxBrightness = brightnessAttribute.AttributeMaxValue; float halfB = maxBrightness / 2; float brightnessMultiplier = 1 + (val - halfB) / halfB; float finalMultiplier = maturityMupltiplier * brightnessMultiplier; SetPlayerStatisticsBasedOn(finalMultiplier); } private void SetPlayerStatisticsBasedOn(float finalMultiplier) { // --- TRINKET INTEGRATION IN STATS CALCULATION --- // 1. Calculate specific total multipliers for UI scaling float totalHealthMult = finalMultiplier * m_trinketStats.healthMult; float totalFaithMult = finalMultiplier * m_trinketStats.faithMult; // Handle 0 default for Stamina float validStaminaMult = m_trinketStats.staminaMult > 0 ? m_trinketStats.staminaMult : 1f; float totalStaminaMult = finalMultiplier * validStaminaMult; // 2. Capture Current Health Percentage BEFORE changes // This prevents the health bar from looking empty when Max HP increases float healthPercent = m_vController.maxHealth > 0 ? m_vController.currentHealth / m_vController.maxHealth : 1f; // 3. Apply Stats // Faith faithRegenMultiplier = sceneDependantFaithRegenMultiplier * finalMultiplier * m_trinketStats.faithRegenMult; faithAttribute.AttributeMaxValue = Mathf.RoundToInt(faithBaseMaxValue * totalFaithMult); // Max Health int newMaxHealth = Mathf.RoundToInt(totalHealthMult * healthBaseMaxValue); m_vController.maxHealth = newMaxHealth; // Apply Proportional Current Health // Uses ChangeHealth() because currentHealth has a protected setter m_vController.ChangeHealth(Mathf.RoundToInt(newMaxHealth * healthPercent)); // Stamina m_vController.maxStamina = Mathf.Round(totalStaminaMult * staminaBaseMaxValue); m_vController.staminaRecovery = (finalMultiplier * staminaBaseRegenValue); // Natural Health Recovery (From Maturity/Brightness) // We set the BASE here. UpdateTrinketStats will later add any trinket bonuses to this value. m_vController.healthRecovery = finalMultiplier * healthBaseRegenValue; // ------------------------------------------------ onStatsUpdated?.Invoke(totalHealthMult, totalFaithMult, totalStaminaMult); } public void UpdatePlayerStatisticsOnMaturityChange(float val = 0, float prevVal = 0) { float maturityMupltiplier = (float)(1 + ((float)val * 0.5f / maturityAttribute.AttributeMaxValue)); float maxBrightness = brightnessAttribute.AttributeMaxValue; float halfB = maxBrightness / 2f; float brightnessMultiplier = 1 + (brightnessAttribute.AttributeCurrentValue - halfB) / halfB; float finalMultiplier = maturityMupltiplier * brightnessMultiplier; SetPlayerStatisticsBasedOn(finalMultiplier); } [Button] public void UpdateMaturityCurrentValue(int valueChange, int prevVal) { float newVal = maturityAttribute.AttributeCurrentValue + valueChange; if (newVal > maturityAttribute.AttributeMaxValue) newVal = maturityAttribute.AttributeMaxValue; else if (newVal < maturityAttribute.AttributeMinValue) newVal = maturityAttribute.AttributeMinValue; maturityAttribute.SetValue(newVal); } public float GetCurrentMaturityValue() { return maturityAttribute.AttributeCurrentValue; } public double GetBrightness() { var attr = GetAttribute("BrightnessPoints"); return (double)attr.AttributeCurrentValue; } public void SetBrightness(double value) { brightnessAttribute.SetValue((int)value); } public double GetMaturity() { var attr = GetAttribute("Maturity"); return (double)attr.AttributeCurrentValue; } public void SetMaturity(double value) { maturityAttribute.SetValue((int)value); } public void UpdateBrightnessCurrentValue(int points) { UpdateBrightnessCurrentValue((float)points); } [Button] public void UpdateBrightnessCurrentValue(float points) { if (points < 0) { float multiplier = 1f; for (int i = 0; i < BrightnessPoints_Th.Length; i++) { if (brightnessAttribute.AttributeCurrentValue < BrightnessPoints_Th[i]) multiplier *= 0.5f; else break; } brightnessAttribute.SetValue(brightnessAttribute.AttributeCurrentValue + (int)(points * multiplier)); } else { if (brightnessAttribute.AttributeCurrentValue < 50) points = points / 2; brightnessAttribute.SetValue(brightnessAttribute.AttributeCurrentValue + points); } } private void OnBrightnessPointsValueChanged(float value, float prevValues) { if (menuScroll.m_isOpen) { ClearOnMenuScrollClosed(); onMenuScrollClosed = () => { BarkBrightnessChange(value, prevValues); }; menuScroll.OnClosed += onMenuScrollClosed; return; } else if (DialogueManager.instance.IsConversationActive) { onMenuScrollClosed = () => { BarkBrightnessChange(value, prevValues); }; var events = DialogueManager.instance.GetComponent(); onDialogueEnded = (transform) => { BarkBrightnessChange(value, prevValues); }; events.conversationEvents.onConversationEnd.AddListener(onDialogueEnded); return; } BarkBrightnessChange(value, prevValues); } public void OnMaturityPointsValueChanged(float value, float prevValue) { DialogueManager.BarkString("Maturity Gained!", Player.Instance.transform); PlayMaturityGained(); } public void BarkBrightnessChange(float value, float prevValues) { float difference = Mathf.Abs(value - prevValues); if (difference < 1f) { } else if (value > prevValues) DialogueManager.BarkString("Brightness Points Gained!", Player.Instance.transform); else DialogueManager.BarkString("Brightness Points Lost!", Player.Instance.transform); ClearOnMenuScrollClosed(); } private void ClearOnMenuScrollClosed() { menuScroll.OnClosed -= onMenuScrollClosed; onMenuScrollClosed = null; if (onDialogueEnded != null) { var events = DialogueManager.instance.GetComponent(); events.conversationEvents.onConversationEnd.RemoveListener(onDialogueEnded); onDialogueEnded = null; } } public float GetCurrentFaithValue() { return faithAttribute.AttributeCurrentValue; } public void UpdateFaithCurrentValue(int valueChange) { UpdateFaithCurrentValue((float)valueChange); } public void UpdateFaithCurrentValue(float valueChange) { float newVal = faithAttribute.AttributeCurrentValue + valueChange; faithAttribute.SetValue(newVal); } public void SetInteractableButtonImage(Sprite image) { if (!defaultInteractionImage) defaultInteractionImage = interactionImage.sprite; interactionImage.sprite = image; } public void ResetIntaractableButtonImage() { if (!defaultInteractionImage) defaultInteractionImage = interactionImage.sprite; interactionImage.sprite = defaultInteractionImage; } public void SetDialogueIntaractableButtonImage() { if (!defaultInteractionImage) defaultInteractionImage = interactionImage.sprite; interactionImage.sprite = dialogueInteractionImage; } public void MoveToTransform(Transform t) { ThirdPersonController._rigidbody.MovePosition(t.position); ThirdPersonController._rigidbody.MoveRotation(t.rotation); } public void PlaySingleSound(AudioClip clipToPlay, bool destroyAfterPlaying = true) { if (Time.timeSinceLevelLoad < 0.5f && clipToPlay != null) { } if (this.audioSource == null) { Debug.LogWarning("Player's 'audioSource' is not assigned."); return; } if (clipToPlay == null) return; GameObject audioObjectInstance = Instantiate(this.audioSource, transform.position, transform.rotation); AudioSource sourceComponent = audioObjectInstance.GetComponent(); if (sourceComponent != null) { sourceComponent.PlayOneShot(clipToPlay); if (destroyAfterPlaying) Destroy(audioObjectInstance, clipToPlay.length + 0.1f); } else Destroy(audioObjectInstance); } } [System.Serializable] public class PlayerAttribute { public string AttributeName; public float AttributeCurrentValue; public float AttributeMaxValue; public float AttributeMinValue = 0; public UnityEvent OnValueChanged; public void SetValue(PlayerAttribute attribute) { if (attribute.AttributeName != AttributeName) return; AttributeMaxValue = attribute.AttributeMaxValue; AttributeMinValue = attribute.AttributeMinValue; SetValue(attribute.AttributeCurrentValue); } public void Init() { DialogueLua.SetVariable(AttributeName, AttributeCurrentValue); } public void SetValue(float value) { if (value != AttributeCurrentValue) { value = value > AttributeMaxValue ? AttributeMaxValue : value; value = value < AttributeMinValue ? AttributeMinValue : value; OnValueChanged?.Invoke(value, AttributeCurrentValue); AttributeCurrentValue = value; DialogueLua.SetVariable(AttributeName, value); } } public void ClearValue() { AttributeCurrentValue = AttributeMinValue; } } }