This commit is contained in:
2026-07-09 21:33:33 +02:00
commit 84a2a365f3
2364 changed files with 950134 additions and 0 deletions
@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Triggers;
namespace VOID.Generic.Objects.Abstract
{
[SelectionBase]
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
public abstract class AbstractObject : MonoBehaviour
{
[Title("=== BasicObject properties ===")]
public AbstractObjectData basicData;
public AbstractObjectEvents basicEvents;
public AbstractObjectStatusManager statusManager;
public AbstractObjectState basicState;
protected void OnValidate()
{
gameObject.GetComponent<Rigidbody>().hideFlags = HideFlags.NotEditable;
// Init RigidBody
var rb = GetComponent<Rigidbody>();
rb.mass = 0;
rb.linearDamping = 0;
rb.angularDamping = 0;
rb.automaticCenterOfMass = true;
rb.automaticInertiaTensor = true;
rb.useGravity = false;
rb.isKinematic = true;
rb.interpolation = RigidbodyInterpolation.None;
rb.constraints = RigidbodyConstraints.FreezeAll;
}
private void Awake()
{
GetObjectComponents().ForEach(component => component.SetParent(this));
GetObjectComponents().ForEach(component => component.DoSelfInitialize());
GetObjectComponents().ForEach(component => component.DoEventInitialize());
GetObjectComponents().ForEach(component => component.DoInitialize());
}
private void Start()
{
// Finally when this object is fully initialized then we can
basicEvents.onSpawn.Invoke();
}
protected void FixedUpdate()
{
basicEvents.onFixedUpdate.Invoke();
}
protected virtual List<ObjectComponent> GetObjectComponents()
{
return new List<ObjectComponent>
{
basicData,
basicEvents,
statusManager,
basicState
};
}
[Button]
public void TakeDamage(DamageEvent damageEvent)
{
if (basicState.isDead) return;
if (!basicData.canBeDestroyed) return;
basicEvents.onDamagedBefore.Invoke(damageEvent);
if (damageEvent.attacker) damageEvent.attacker.unitEvents.onDamageBefore.Invoke(damageEvent);
if (damageEvent.prevented) return;
var damage = damageEvent.amount;
var resistance = 1 - basicData.resistances.Get(damageEvent.damageType);
basicData.UseResource(BasicResourceType.Health, damage * resistance, true);
basicEvents.onDamagedAfter.Invoke(damageEvent);
if (damageEvent.attacker) damageEvent.attacker.unitEvents.onDamageAfter.Invoke(damageEvent);
if (basicData.currentResources.health <= 0f) Death();
}
[Button]
public void Death()
{
if (basicState.isDead) return;
if (!basicData.canBeDestroyed) return;
var deathEvent = new DeathEvent { obj = this };
basicEvents.onDeathBefore.Invoke(deathEvent);
if (deathEvent.prevented) return;
basicEvents.onDeathAfter.Invoke(deathEvent);
basicData.currentResources.health = 0f;
}
[Button]
public void Revive()
{
if (basicState.isDead == false) return;
basicData.SetResourcePercentage(BasicResourceType.Health, 0.3f);
basicEvents.onRevive.Invoke(new ReviveEvent { obj = this });
}
[Button]
public void Remove()
{
var removeEvent = new RemoveEvent { obj = this };
basicEvents.onRemove.Invoke(removeEvent);
Destroy(gameObject);
}
private void OnTriggerEnter(Collider other)
{
other.GetComponentsInParent<IDynamicTrigger>().ForEach(triggerArea => {
if (basicState.isDead == false) triggerArea.OnObjectEnter(this);
basicEvents.onTriggerEnter.Invoke(new TriggerEvent { obj = this, triggerArea = triggerArea });
});
}
private void OnTriggerExit(Collider other)
{
other.GetComponentsInParent<IDynamicTrigger>().ForEach(triggerArea => {
if (basicState.isDead == false) triggerArea.OnObjectExit(this);
basicEvents.onTriggerExit.Invoke(new TriggerEvent { obj = this, triggerArea = triggerArea });
});
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a31cf06e6e17cc54cb2c67298dbc5cfc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 85c7ecfed11eec14daa605a62c6639ab, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,144 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Abstract.Data;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Objects.Item;
using Resources = VOID.Generic.Objects.Abstract.Data.Resources;
namespace VOID.Generic.Objects.Abstract
{
/// <summary>
/// Contain and manages all generic data needed and used by <see cref="AbstractObject"/> or its children.
/// </summary>
[Serializable]
public class AbstractObjectData : ObjectComponent<AbstractObject>
{
#region Initialize
protected override void SelfInitialize()
{
// Init resources values
foreach (BasicResourceType resourceType in Enum.GetValues(typeof(BasicResourceType)))
maxResources.Set(resourceType, baseMaxResources.Get(resourceType));
// Init resistances values
foreach (DamageType damageType in Enum.GetValues(typeof(DamageType)))
resistances.Set(damageType, baseResistances.Get(damageType));
}
protected override void Initialize()
{
// Instantiate stats
baseMaxResources = baseMaxResources with {};
maxResources = maxResources with {};
currentResources = currentResources with {};
baseResistances = baseResistances with {};
resistances = resistances with {};
}
#endregion
[Title("Possible actions")]
[LabelText("TODO: canBeSelected")] public bool canBeSelected = true;
public bool canBeDestroyed = true;
[LabelText("TODO: canBeInspected")] public bool canBeInspected = true;
[Title("Display in UI")]
public string name;
public string finalName => parent switch
{
ItemObject item => item.itemData.finalName,
_ => name
};
public Texture2D image;
[TextArea] public string description;
[Title("Size")]
[MinValue(1f)] public float height = 1f;
[MinValue(.25f)] public float radius = 0.25f;
[Title("Basic Resources")]
public Resources baseMaxResources;
[ReadOnly] public Resources maxResources;
[ReadOnly] public Resources currentResources;
[Title("Basic Resistances")]
public Resistances baseResistances;
[ReadOnly] public Resistances resistances;
/// <summary>
/// Uses given resource by given amount.<br/>
/// That means positive values decreases and negative values increases that resource.
/// </summary>
/// <param name="resourceType">Resource to use</param>
/// <param name="change">Amount to change</param>
/// <param name="force">If TRUE then we can use this resource even if we don't have enough of it</param>
/// <returns>Returns TRUE if done successfully.</returns>
public bool UseResource(BasicResourceType 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.basicEvents.onCurrentResourceChange.Invoke(new BasicResourceChangeEvent
{
obj = parent,
type = resourceType,
oldValue = oldValue,
newValue = oldValue - change,
changedValue = change,
});
return true;
}
/// <summary>
/// Sets given resource to given value.<br/>
/// It uses <see cref="UseResource"/> so whole process will be executed as change from starting value to given value.
/// </summary>
/// <param name="resourceType">Resource type to set</param>
/// <param name="value">Value to set</param>
/// <returns>Return TRUE if done successfully.</returns>
public bool SetResource(BasicResourceType 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);
}
/// <summary>
/// Sets given resource to percentage of its max value.<br/>
/// It uses <see cref="SetResource"/> so whole process will be executed as change from starting value to given value.
/// </summary>
/// <param name="resourceType">Resource type to set</param>
/// <param name="percentage">float as percent, value needs to be between 0 and 1</param>
/// <returns>Return TRUE if done successfully.</returns>
public bool SetResourcePercentage(BasicResourceType 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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a121af7cd1dd8b74aab63b7fba7ccd19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,63 @@
using Sirenix.OdinInspector;
using UnityEngine.Events;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Objects.Unit.Events;
namespace VOID.Generic.Objects.Abstract
{
[System.Serializable]
public class AbstractObjectEvents : ObjectComponent<AbstractObject>
{
[Title("Spawn")]
public UnityEvent onSpawn;
[Title("Destroy")]
public UnityEvent<RemoveEvent> onRemove;
[Title("Time")]
public UnityEvent onFixedUpdate;
// COMPONENT RELATED EVENTS
[Title("Statuses")]
public UnityEvent<StatusEvent> onStatusStart;
public UnityEvent<StatusEvent> onStatusEnd;
public UnityEvent<EffectEvent> onEffectStart;
public UnityEvent<EffectEvent> onEffectEnd;
[Title("Data")]
public UnityEvent<BasicResourceChangeEvent> onMaxResourceChange;
public UnityEvent<BasicResourceChangeEvent> onCurrentResourceChange;
// TURN RELATED EVENTS
// These should be ran only from `VOID.Generic.Turn.TurnManager`
// So all `Invoke()` related to turns will be inside that manager class
[Title("Turn")]
public UnityEvent<TurnEvent> onTurnQueueStart;
public UnityEvent<TurnEvent> onTurnQueueEnd;
public UnityEvent<TurnEvent> onTurnQueueSwap;
public UnityEvent<TurnEvent> onTurnQueueEnter;
public UnityEvent<TurnEvent> onTurnQueueLeave;
public UnityEvent<TurnEvent> onTurnSelfStart;
public UnityEvent<TurnEvent> onTurnSelfEnd;
// ACTION RELATED EVENTS
[Title("Death")]
public UnityEvent<DeathEvent> onDeathBefore;
public UnityEvent<DeathEvent> onDeathAfter;
[Title("Death")]
public UnityEvent<ReviveEvent> onRevive;
[Title("Being attacked")]
public UnityEvent<DamageEvent> onDamagedBefore;
public UnityEvent<DamageEvent> onDamagedAfter;
[Title("Being hit by ability")]
public UnityEvent<AbilityHitEvent> onAbilityHitBefore;
public UnityEvent<AbilityHitEvent> onAbilityHitAfter;
[Title("Being inspected")]
public UnityEvent onInspectedBefore;
public UnityEvent onInspectedAfter;
// PHYSICS RELATED EVENTS
[Title("TRIGGER ENTER & LEAVE")]
public UnityEvent<TriggerEvent> onTriggerEnter;
public UnityEvent<TriggerEvent> onTriggerExit;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6976257b5e64b2e828f7a5321fd7c21
timeCreated: 1664306673
@@ -0,0 +1,78 @@
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Triggers;
using VOID.Generic.Turn;
namespace VOID.Generic.Objects.Abstract
{
[System.Serializable]
public class AbstractObjectState : ObjectComponent<AbstractObject>
{
#region Initialize
protected override void EventInitialize()
{
// Death & Revive events
parent.basicEvents.onDeathAfter.AddListener(OnDeath);
parent.basicEvents.onRevive.AddListener(OnRevive);
// Turn events
parent.basicEvents.onTurnQueueEnter.AddListener(OnTurnEnter);
parent.basicEvents.onTurnQueueLeave.AddListener(OnTurnLeave);
parent.basicEvents.onTurnQueueSwap.AddListener(OnTurnSwap);
// Trigger events
parent.basicEvents.onTriggerEnter.AddListener(OnTriggerEnter);
parent.basicEvents.onTriggerExit.AddListener(OnTriggerExit);
}
#endregion
[ShowInInspector, ReadOnly] public TurnManager turnManager { get; private set; }
private List<IDynamicTrigger> _triggerAreas = new();
public bool isDead;
private void OnTurnEnter(TurnEvent turnEvent)
{
turnManager = turnEvent.turnManager;
}
private void OnTurnLeave(TurnEvent turnEvent)
{
turnManager = null;
}
private void OnTurnSwap(TurnEvent turnEvent)
{
turnManager = turnEvent.turnManager;
}
private void OnDeath(DeathEvent deathEvent)
{
isDead = true;
_triggerAreas.RemoveAll(triggerArea => triggerArea as MonoBehaviour == null);
_triggerAreas.ForEach(triggerArea => triggerArea.OnObjectExit(parent));
if (turnManager) turnManager.ObjectExit(parent);
}
private void OnRevive(ReviveEvent reviveEvent)
{
isDead = false;
_triggerAreas.RemoveAll(triggerArea => triggerArea as MonoBehaviour == null);
_triggerAreas.ForEach(triggerArea => triggerArea.OnObjectEnter(parent));
}
private void OnTriggerEnter(TriggerEvent triggerEvent)
{
_triggerAreas.Add(triggerEvent.triggerArea);
}
private void OnTriggerExit(TriggerEvent triggerEvent)
{
_triggerAreas.Remove(triggerEvent.triggerArea);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c981ec7d6b04481befd7320d974b587
timeCreated: 1686607159
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Objects.Unit;
using VOID.ScriptableObjects;
namespace VOID.Generic.Objects.Abstract
{
[System.Serializable]
public class AbstractObjectStatusManager : ObjectComponent<AbstractObject>
{
#region Initialize
protected override void EventInitialize()
{
// When realtime based gameplay is working
parent.basicEvents.onFixedUpdate.AddListener(OnRealTimeDecreaseDuration);
// When turn based gameplay is working
parent.basicEvents.onTurnSelfStart.AddListener(OnTurnSelfStart);
parent.basicEvents.onTurnSelfEnd.AddListener(OnTurnSelfEnd);
// When entering or leaving turn based gameplay
parent.basicEvents.onTurnQueueEnter.AddListener(OnTurnEnter);
parent.basicEvents.onTurnQueueLeave.AddListener(OnTurnLeave);
}
protected override void Initialize()
{
// Statuses already added via inspector need to be started this way
// Important thing - instantiate ScriptableObjects so we wont change original ones
for (var i = 0; i < statuses.Count; i++)
{
statuses[i] = Object.Instantiate(statuses[i]);
statuses[i].Init(parent);
statuses[i].Apply();
}
}
#endregion
[SerializeReference, DisableInPlayMode] private List<BasicStatus> statuses = new();
private void OnTurnEnter(TurnEvent turnEvent) => parent.basicEvents.onFixedUpdate.RemoveListener(OnRealTimeDecreaseDuration);
private void OnTurnLeave(TurnEvent turnEvent) => parent.basicEvents.onFixedUpdate.AddListener(OnRealTimeDecreaseDuration);
private void OnTurnSelfStart(TurnEvent turnEvent) => statuses.ForEach(status => status.OnTurnSelfStart());
private void OnTurnSelfEnd(TurnEvent turnEvent) => statuses.ToList().ForEach(status => status.OnTurnSelfEnd());
private void OnRealTimeDecreaseDuration() => statuses.ToList().ForEach(status => status.DecreaseDurationRealTime());
public void AddStatus(BasicStatus status, UnitObject source = null, bool instantiate = true)
{
// instantiate ScriptableObjects so we wont change original ones if needed
if (instantiate) status = Object.Instantiate(status);
statuses.Add(status);
status.Init(parent, source);
status.Apply();
}
public void RemoveStatus(BasicStatus status) => statuses.Remove(status);
public BasicStatus[] GetStatuses() => statuses.ToArray();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1383959980310a349900e380980f8b6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c45b5a8066dd4cbd9656c671b7b4feb7
timeCreated: 1693742336
@@ -0,0 +1,56 @@
using System;
using VOID.Generic.Dict;
namespace VOID.Generic.Objects.Abstract.Data
{
[Serializable]
public record Resistances
{
public float physicalResistance = 0f;
public float magicalResistance = 0f;
public float fireResistance = 0f;
public float waterResistance = 0f;
public float earthResistance = 0f;
public float airResistance = 0f;
public float poisonResistance = 0f;
public float lightResistance = 0f;
public float darkResistance = 0f;
public float healResistance = 2f;
public float Get(DamageType type)
{
return type switch
{
DamageType.Physical => physicalResistance,
DamageType.Magical => magicalResistance,
DamageType.Fire => fireResistance,
DamageType.Water => waterResistance,
DamageType.Earth => earthResistance,
DamageType.Air => airResistance,
DamageType.Poison => poisonResistance,
DamageType.Light => lightResistance,
DamageType.Dark => darkResistance,
DamageType.Heal => healResistance,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
public void Set(DamageType type, float value)
{
switch(type)
{
case DamageType.Physical: physicalResistance = value; return;
case DamageType.Magical: magicalResistance = value; return;
case DamageType.Fire: fireResistance = value; return;
case DamageType.Water: waterResistance = value; return;
case DamageType.Earth: earthResistance = value; return;
case DamageType.Air: airResistance = value; return;
case DamageType.Poison: poisonResistance = value; return;
case DamageType.Light: lightResistance = value; return;
case DamageType.Dark: darkResistance = value; return;
case DamageType.Heal: healResistance = value; return;
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
};
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e2097634927841769bcd90360a511ed1
timeCreated: 1693742351
@@ -0,0 +1,31 @@
using System;
using Sirenix.OdinInspector;
using VOID.Generic.Dict;
namespace VOID.Generic.Objects.Abstract.Data
{
[Serializable]
public record Resources
{
[MinValue(0f)]
public float health = 10f;
public float Get(BasicResourceType type)
{
return type switch
{
BasicResourceType.Health => health,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
public void Set(BasicResourceType type, float value)
{
switch(type)
{
case BasicResourceType.Health: health = value; return;
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
};
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9a3fdf71177419da626c4f3ba6b0b27
timeCreated: 1693743887
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8f294cc39c5481d985ccf4dd724f25d
timeCreated: 1668634664
@@ -0,0 +1,7 @@
namespace VOID.Generic.Objects.Abstract.Events
{
public abstract class AbstractEvent
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1ee6c0ca0efc40f8a2a0605b355f90f3
timeCreated: 1666901839
@@ -0,0 +1,13 @@
using VOID.Generic.Dict;
namespace VOID.Generic.Objects.Abstract.Events
{
public class BasicResourceChangeEvent : AbstractEvent
{
public AbstractObject obj;
public BasicResourceType type;
public float oldValue;
public float newValue;
public float changedValue;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 767da03874a249c99966ae86ee48ae8f
timeCreated: 1693752577
@@ -0,0 +1,15 @@
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Objects.Abstract.Events
{
public class DamageEvent : AbstractEvent
{
public bool prevented = false;
public AbstractObject target;
public UnitObject attacker;
public float amount;
public DamageType damageType;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bca6a131b1c94d9ba67836be72a4c62c
timeCreated: 1668636901
@@ -0,0 +1,9 @@
namespace VOID.Generic.Objects.Abstract.Events
{
public class DeathEvent : AbstractEvent
{
public bool prevented = false;
public AbstractObject obj;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a7373f77231b4c27b49e8352494e9fe3
timeCreated: 1675363800
@@ -0,0 +1,10 @@
using VOID.Generic.Status.Effect;
namespace VOID.Generic.Objects.Abstract.Events
{
public class EffectEvent : AbstractEvent
{
public AbstractObject obj;
public BasicEffect effect;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7540cd1428134f3884aa1660037ebbd6
timeCreated: 1674413226
@@ -0,0 +1,7 @@
namespace VOID.Generic.Objects.Abstract.Events
{
public class RemoveEvent : AbstractEvent
{
public AbstractObject obj;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a06b6ad4d60443bb4f6421a8685ed8f
timeCreated: 1675789635
@@ -0,0 +1,7 @@
namespace VOID.Generic.Objects.Abstract.Events
{
public class ReviveEvent : AbstractEvent
{
public AbstractObject obj;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 83a0d26632374a0a8a32b803e5c70a89
timeCreated: 1710964438
@@ -0,0 +1,10 @@
using VOID.ScriptableObjects;
namespace VOID.Generic.Objects.Abstract.Events
{
public class StatusEvent : AbstractEvent
{
public AbstractObject obj;
public BasicStatus status;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52a97addb7b74e31b985f8319b3c3d50
timeCreated: 1670355516
@@ -0,0 +1,10 @@
using VOID.Generic.Triggers;
namespace VOID.Generic.Objects.Abstract.Events
{
public class TriggerEvent : AbstractEvent
{
public AbstractObject obj;
public IDynamicTrigger triggerArea;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c2a7cd182ef417da291d870148e8524
timeCreated: 1710969295
@@ -0,0 +1,10 @@
using VOID.Generic.Turn;
namespace VOID.Generic.Objects.Abstract.Events
{
public class TurnEvent : AbstractEvent
{
public AbstractObject obj;
public TurnManager turnManager;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0d2a167160234911a80d008f65ba2154
timeCreated: 1696968391