using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit.Data;
using VOID.Generic.Objects.Unit.Events;
using VOID.Generic.Objects.Weapon;
namespace VOID.Generic.Objects.Unit
{
///
/// Contain and manages all generic data needed and used by or its children.
///
[System.Serializable]
public class UnitObjectData : ObjectComponent
{
#region Initialize
protected override void SelfInitialize()
{
// Instantiate stats
baseMaxResources = baseMaxResources with {};
maxResources = maxResources with {};
currentResources = currentResources with {};
baseMainAttributes = baseMainAttributes with {};
mainAttributes = mainAttributes with {};
baseSubAttributes = baseSubAttributes with {};
subAttributes = subAttributes with {};
}
protected override void EventInitialize()
{
RegisterUpdateWeaponRelatedAttributes();
}
#endregion
#region Events
private void RegisterUpdateWeaponRelatedAttributes()
{
parent.unitEvents.onEquipmentEquip.AddListener(equipEvent =>
{
if (equipEvent.wearable is WeaponObject) UpdateWeaponRelatedAttributes();
});
parent.unitEvents.onEquipmentUnEquip.AddListener(equipEvent =>
{
if (equipEvent.wearable is WeaponObject) UpdateWeaponRelatedAttributes();
});
}
///
/// When changing weapons also inherit 's damage and critical attributes.
///
private void UpdateWeaponRelatedAttributes()
{
var mainWeapon = parent.unitEquipment.GetEquipped(EquipmentSlotType.MainHand) as WeaponObject;
var offWeapon = parent.unitEquipment.GetEquipped(EquipmentSlotType.OffHand) as WeaponObject;
if (mainWeapon)
{
baseSubAttributes.damageMin = mainWeapon.weaponData.damageMin;
baseSubAttributes.damageMax = mainWeapon.weaponData.damageMax;
baseSubAttributes.criticalDamageChance = mainWeapon.weaponData.criticalDamageChange;
baseSubAttributes.criticalDamageMultiplier = mainWeapon.weaponData.criticalDamageMultiplier;
if (offWeapon)
{
baseSubAttributes.damageMin += offWeapon.weaponData.damageMin;
baseSubAttributes.damageMax += offWeapon.weaponData.damageMax;
}
}
else if (offWeapon)
{
baseSubAttributes.damageMin = offWeapon.weaponData.damageMin;
baseSubAttributes.damageMax = offWeapon.weaponData.damageMax;
baseSubAttributes.criticalDamageChance = offWeapon.weaponData.criticalDamageChange;
baseSubAttributes.criticalDamageMultiplier = offWeapon.weaponData.criticalDamageMultiplier;
}
else
{
baseSubAttributes.damageMin = 1;
baseSubAttributes.damageMax = 1;
baseSubAttributes.criticalDamageChance = 0;
baseSubAttributes.criticalDamageMultiplier = 1;
}
// Recalc those attributes
parent.unitDataCalculator.Queue(SubAttributeType.DamageMin);
parent.unitDataCalculator.Queue(SubAttributeType.DamageMax);
parent.unitDataCalculator.Queue(SubAttributeType.CriticalDamageChance);
parent.unitDataCalculator.Queue(SubAttributeType.CriticalDamageMultiplier);
}
#endregion
[Title("Possible actions")]
public bool canBeTalked;
[Title("Possible actions to do")]
public bool canMove = true;
public bool canAttack = true;
public bool canUse = true;
public bool canTalk = true;
public bool canTake = true;
public bool canInspect = true;
[Title("Unit Resources")]
public UnitResources baseMaxResources;
[ReadOnly] public UnitResources maxResources;
[ReadOnly] public UnitResources currentResources;
[Title("Main Attributes")]
public MainAttributes baseMainAttributes;
[ReadOnly] public MainAttributes mainAttributes;
[Title("Sub Attributes")]
public SubAttributes baseSubAttributes;
[ReadOnly] public SubAttributes subAttributes;
[Title("Interaction")]
[MinValue(1f)] public float useRange = 1f;
[MinValue(1f)] public float talkRange = 2f;
[MinValue(1f)] public float takeRange = 2f;
[MinValue(1f)] public float inspectRange = 20f;
[Title("Offset positions")]
[Required] public Transform castPosition;
[Required] public Transform ladderPosition;
[Required] public Transform rightHandPosition;
[Required] public Transform leftHandPosition;
[Title("Visual")]
public GameObject topUnderwear;
public GameObject bottomUnderwear;
///
/// Uses given resource by given amount.
/// That means positive values decreases and negative values increases that resource.
///
/// Resource to use
/// Amount to change
/// If TRUE then we can use this resource even if we dont have enough of it
/// Returns TRUE if done successfully.
public bool UseResource(UnitResourceType resourceType, float change, bool force = false)
{
var oldValue = currentResources.Get(resourceType);
var maxValue = maxResources.Get(resourceType);
// Attempt to use more than available
if (oldValue - change < 0f)
{
// If not forced - not enough resource
if (!force) return false;
// Else - use only remaining resource
change += oldValue - change;
}
// Attempt to go over limit
if (oldValue - change > maxValue)
{
change = oldValue - maxValue;
}
currentResources.Set(resourceType, oldValue - change);
parent.unitEvents.onCurrentResourceChange.Invoke(new UnitResourceChangeEvent
{
unit = parent,
type = resourceType,
oldValue = oldValue,
newValue = oldValue - change,
changedValue = change,
});
return true;
}
///
/// Sets given resource to given value.
/// It uses so whole process will be executed as change from starting value to given value.
///
/// Resource type to set
/// Value to set
/// Return TRUE if done successfully.
public bool SetResource(UnitResourceType resourceType, float value)
{
var max = maxResources.Get(resourceType);
var oldValue = Mathf.Clamp(currentResources.Get(resourceType), 0f, max);
var change = oldValue - value;
return UseResource(resourceType, change, true);
}
///
/// Sets given resource to percentage of its max value.
/// It uses so whole process will be executed as change from starting value to given value.
///
/// Resource type to set
/// float as percent, value needs to be between 0 and 1
/// Return TRUE if done successfully.
public bool SetResourcePercentage(UnitResourceType resourceType, float percentage)
{
percentage = Mathf.Clamp01(percentage);
var max = maxResources.Get(resourceType);
var value = percentage * max;
var oldValue = currentResources.Get(resourceType);
var change = oldValue - value;
return UseResource(resourceType, change, true);
}
}
}