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,3 @@
fileFormatVersion: 2
guid: 1e8e769129ee4c1b9adaf2335158ae9b
timeCreated: 1709165760
@@ -0,0 +1,84 @@
using System;
using System.Linq;
using Schema;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Dict.Ability;
using VOID.Generic.Util;
using VOID.ScriptableObjects.Abilities;
using Action = Schema.Action;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets ability by selected filters from this unit and save it as blackboard variable")]
[Category(SchemaCategory.ThisUnit)]
public class GetAbility : Action
{
private enum SearchType
{
First,
Random,
LeastExpensive,
MostExpensive,
GreatestRange
}
private enum AbilityPurposeType
{
Any,
All
}
[SerializeField] private bool includeBasicAttack;
[SerializeField] private BlackboardEntrySelector<float> atLeastRange;
[SerializeField] private AbilityPurposeType abilityPurposeType;
[SerializeField] private AbilityPurposeFlag abilityPurpose;
[SerializeField] private SearchType searchBy;
[SerializeField, WriteOnly, Space(15)] private BlackboardEntrySelector<BasicAbility> saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
var allAbilities = thisUnit.unitAbilityBook.abilities.ToList();
// Unit dont have any abilities
if (allAbilities.IsNullOrEmpty()) return NodeStatus.Failure;
if (includeBasicAttack) allAbilities.Add(thisUnit.unitAbilityBook.attackAbility);
// Filter by range required
allAbilities = allAbilities.Where(ability => ability.range >= atLeastRange.value).ToList();
// Filter by wanted purpose
allAbilities = allAbilities.Where(ability =>
{
switch (abilityPurposeType)
{
case AbilityPurposeType.All when (ability.abilityPurpose & abilityPurpose) == abilityPurpose:
case AbilityPurposeType.Any when (ability.abilityPurpose & abilityPurpose) != AbilityPurposeFlag.None:
return true;
default:
return false;
}
}).ToList();
// Getting final ability and saving it
saveTo.value = searchBy switch
{
SearchType.First => allAbilities.FirstOrDefault(),
SearchType.Random => RandomUtil.RandomElement(allAbilities),
SearchType.LeastExpensive => allAbilities.Aggregate(allAbilities.First(),
(a1, a2) => a2.actionPointCost > a1.actionPointCost ? a1 : a2),
SearchType.MostExpensive => allAbilities.Aggregate(allAbilities.First(),
(a1, a2) => a2.actionPointCost <= a1.actionPointCost ? a1 : a2),
SearchType.GreatestRange => allAbilities.Aggregate(allAbilities.First(),
(a1, a2) => a2.range <= a1.range ? a1 : a2),
_ => throw new ArgumentOutOfRangeException()
};
if (!saveTo.value) return NodeStatus.Failure;
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0912de9aef0840feb6e15e730b800677
timeCreated: 1709738283
@@ -0,0 +1,23 @@
using Schema;
using UnityEngine;
using VOID.ScriptableObjects.Abilities;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets basic attack ability from this unit and save it as blackboard variable")]
[Category(SchemaCategory.ThisUnit)]
public class GetAttackAbility : Action
{
[SerializeField, WriteOnly] public BlackboardEntrySelector<BasicAbility> _saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
_saveTo.value = thisUnit.unitAbilityBook.attackAbility;
if (!_saveTo.value) return NodeStatus.Failure;
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60fa739b5d604f3b9e0a0155377b4692
timeCreated: 1710064236
@@ -0,0 +1,52 @@
using System;
using Schema;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Util;
using Action = Schema.Action;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets distance between ThisUnit and target position or unit, minus radius of both of units")]
[Category(SchemaCategory.ThisUnit)]
public class GetDistanceTo : Action
{
private enum DistanceFrom
{
Current,
Another
}
private enum DistanceTo
{
Position,
Unit
}
[SerializeField] private DistanceFrom _distanceFrom;
[SerializeField, ShowIf(nameof(_distanceFrom), DistanceFrom.Another)] private BlackboardEntrySelector<Vector3> _position;
[SerializeField, Space(15)] private DistanceTo _distanceTo;
[SerializeField, ShowIf(nameof(_distanceTo), DistanceTo.Position)] private BlackboardEntrySelector<Vector3> _toPosition;
[SerializeField, ShowIf(nameof(_distanceTo), DistanceTo.Unit)] private BlackboardEntrySelector<UnitObject> _toUnit;
[WriteOnly, SerializeField, Space(15)] private BlackboardEntrySelector<float> _saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var unitAgent = (UnitObjectSchemaAgent)agent;
var thisUnit = unitAgent.unit;
_saveTo.value = (_distanceFrom, _distanceTo) switch
{
(DistanceFrom.Current, DistanceTo.Position) => RangeUtil.GetDistance(thisUnit, _toPosition.value),
(DistanceFrom.Current, DistanceTo.Unit) => RangeUtil.GetDistance(thisUnit, _toUnit.value),
(DistanceFrom.Another, DistanceTo.Position) => RangeUtil.GetDistance(_toPosition.value, _position.value) - thisUnit.basicData.radius,
(DistanceFrom.Another, DistanceTo.Unit) => RangeUtil.GetDistance(_toUnit.value, _position.value) - thisUnit.basicData.radius,
_ => throw new ArgumentOutOfRangeException()
};
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3deac86e81441299d91ee926c0015af
timeCreated: 1726666687
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Schema;
using UnityEngine;
using UnityEngine.AI;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Util;
using Action = Schema.Action;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets point where ThisUnit can go with current MovementPoints towards given destination point")]
[Category(SchemaCategory.CombatThisUnit)]
public class GetReachablePosition : Action
{
private enum DestinationType
{
Position,
Unit
}
[SerializeField] private DestinationType _destinationType;
[SerializeField] private BlackboardEntrySelector<UnitObject> _toUnit;
[SerializeField] private BlackboardEntrySelector<Vector3> _toPosition;
[SerializeField, WriteOnly, Space(15)] private BlackboardEntrySelector<Vector3> _saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
if (!thisUnit.basicState.turnManager) return NodeStatus.Failure;
// Whole path to destination
var path = _destinationType switch
{
DestinationType.Position => GetPathToPosition(thisUnit),
DestinationType.Unit => GetPathToUnit(thisUnit),
_ => throw new ArgumentOutOfRangeException()
};
var movePoints = thisUnit.unitData.currentResources.movePoints;
// Path can't be calculated
if (thisUnit.unitNavAgent.GetPath().status is NavMeshPathStatus.PathInvalid) return NodeStatus.Failure;
// Reachable path (within move points range) and its last possible point
NavMeshPathUtil.SplitPath(path.ToArray(), movePoints, out var reachablePath, out _);
_saveTo.value = reachablePath.Last();
return NodeStatus.Success;
}
private List<Vector3> GetPathToPosition(UnitObject thisUnit)
{
thisUnit.unitNavAgent.CalculatePathTo(_toPosition.value);
return thisUnit.unitNavAgent.GetCorrectedPath();
}
private List<Vector3> GetPathToUnit(UnitObject thisUnit)
{
thisUnit.unitNavAgent.CalculatePathTo(_toUnit.value);
return thisUnit.unitNavAgent.GetCorrectedPath();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 74aab9b029674f94ac8347a3683ca308
timeCreated: 1726254338
@@ -0,0 +1,19 @@
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets this unit and save it as blackboard variable")]
[Category(SchemaCategory.ThisUnit)]
public class GetThisUnit : Action
{
[SerializeField, WriteOnly] public BlackboardEntrySelector<UnitObject> _saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
_saveTo.value = ((UnitObjectSchemaAgent)agent).unit;
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a40ad4564276481f831e44fd55979d34
timeCreated: 1709499899
@@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Linq;
using Schema;
using UnityEngine;
using UnityEngine.AI;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Util;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Gets unit by selected filters and save it as blackboard variable")]
[Category(SchemaCategory.CombatThisUnit)]
public class GetUnit : Action
{
private enum UnitType
{
Any,
Enemy,
Neutral,
Friendly
}
private enum SearchType
{
ClosestByDistance,
FarthestByDistance,
Random,
LeastHp,
MostHp,
ClosestByPath,
FarthestByPath
}
[SerializeField] private UnitType _findUnitBy;
[SerializeField] private SearchType _searchBy;
[SerializeField, Space(15)] private BlackboardEntrySelector<UnitObject> _saveTo;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
if (!thisUnit.basicState.turnManager) return NodeStatus.Failure;
// Get all participants in combat except current unit
var allUnits = thisUnit.basicState.turnManager.allUnitsInQueue.Where(otherUnit => otherUnit != thisUnit)
.ToList();
// Filter by UnitType
if (_findUnitBy is UnitType.Enemy) allUnits = GetEnemy(thisUnit, allUnits);
if (_findUnitBy is UnitType.Neutral) allUnits = GetNeutral(thisUnit, allUnits);
if (_findUnitBy is UnitType.Friendly) allUnits = GetFriendly(thisUnit, allUnits);
// Filter by SearchType
if (_searchBy is SearchType.ClosestByDistance) _saveTo.value = GetClosestByDistance(thisUnit, allUnits);
if (_searchBy is SearchType.FarthestByDistance) _saveTo.value = GetFarthestByDistance(thisUnit, allUnits);
if (_searchBy is SearchType.Random) _saveTo.value = RandomUtil.RandomElement(allUnits);
if (_searchBy is SearchType.LeastHp) _saveTo.value = GetByLeastHp(allUnits);
if (_searchBy is SearchType.MostHp) _saveTo.value = GetByMostHp(allUnits);
if (_searchBy is SearchType.ClosestByPath) _saveTo.value = GetClosestByPath(thisUnit, allUnits);
if (_searchBy is SearchType.FarthestByPath) _saveTo.value = GetFarthestByPath(thisUnit, allUnits);
// Could not find anything
if (!_saveTo.value) return NodeStatus.Failure;
return NodeStatus.Success;
}
private List<UnitObject> GetEnemy(UnitObject thisUnit, List<UnitObject> units)
{
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) < AttitudeType.Neutral)
.ToList();
}
private List<UnitObject> GetNeutral(UnitObject thisUnit, List<UnitObject> units)
{
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) == AttitudeType.Neutral)
.ToList();
}
private List<UnitObject> GetFriendly(UnitObject thisUnit, List<UnitObject> units)
{
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) > AttitudeType.Neutral)
.ToList();
}
private UnitObject GetClosestByDistance(UnitObject thisUnit, List<UnitObject> units)
{
var closestDistance = float.MaxValue;
var closestUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
var distance = RangeUtil.GetDistance(thisUnit, otherUnit);
if (distance >= closestDistance) return;
closestDistance = distance;
closestUnit = otherUnit;
});
return closestUnit;
}
private UnitObject GetFarthestByDistance(UnitObject thisUnit, List<UnitObject> units)
{
var farthestDistance = float.MinValue;
var farthestUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
var distance = RangeUtil.GetDistance(thisUnit, otherUnit);
if (distance <= farthestDistance) return;
farthestDistance = distance;
farthestUnit = otherUnit;
});
return farthestUnit;
}
private UnitObject GetClosestByPath(UnitObject thisUnit, List<UnitObject> units)
{
var closestPathLength = float.MaxValue;
var closestUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
thisUnit.unitNavAgent.CalculatePathTo(otherUnit.transform.position);
var path = thisUnit.unitNavAgent.GetPath();
var pathLength = NavMeshPathUtil.GetPathLength(path);
if (path.status is not NavMeshPathStatus.PathInvalid)
pathLength += Vector3.Distance(path.corners.Last(), otherUnit.transform.position);
if (pathLength >= closestPathLength) return;
closestPathLength = pathLength;
closestUnit = otherUnit;
});
return closestUnit;
}
private UnitObject GetFarthestByPath(UnitObject thisUnit, List<UnitObject> units)
{
var farthestPathLength = float.MinValue;
var farthestUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
thisUnit.unitNavAgent.CalculatePathTo(otherUnit.transform.position);
var path = thisUnit.unitNavAgent.GetPath();
var pathLength = NavMeshPathUtil.GetPathLength(path);
if (path.status is not NavMeshPathStatus.PathInvalid)
pathLength += Vector3.Distance(path.corners.Last(), otherUnit.transform.position);
if (pathLength <= farthestPathLength) return;
farthestPathLength = pathLength;
farthestUnit = otherUnit;
});
return farthestUnit;
}
private UnitObject GetByLeastHp(List<UnitObject> units)
{
var leastHp = float.MinValue;
var leastHpUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
var hp = otherUnit.basicData.currentResources.health;
if (hp >= leastHp) return;
leastHp = hp;
leastHpUnit = otherUnit;
});
return leastHpUnit;
}
private UnitObject GetByMostHp(List<UnitObject> units)
{
var mostHp = float.MaxValue;
var mostHpUnit = (UnitObject)null;
units.ForEach(otherUnit =>
{
var hp = otherUnit.basicData.currentResources.health;
if (hp <= mostHp) return;
mostHp = hp;
mostHpUnit = otherUnit;
});
return mostHpUnit;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a79026c80b3d42cc922bba86f43a82bb
timeCreated: 1709555651
@@ -0,0 +1,22 @@
using Schema;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Actions.Exceptions;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Starts turn based fight")]
[Category(SchemaCategory.CombatThisUnit)]
public class UnitEndTurn : Action
{
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var unitAgent = (UnitObjectSchemaAgent)agent;
if (!unitAgent.unit || !unitAgent.unit.basicState.turnManager || !unitAgent.unit.unitState.isSelfTurn)
return NodeStatus.Failure;
unitAgent.unit.OrderInstantly(new EndTurnAction(unitAgent.unit));
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb47dac215e44eae810d2d4d1537bff5
timeCreated: 1709573630
@@ -0,0 +1,93 @@
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Events;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("This unit will move to given point")]
[Category(SchemaCategory.ThisUnit)]
public class UnitMoveTo : Action
{
private class UnitMoveToMemory
{
public SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory> onMoveEnd;
public bool isMoving;
public bool isFinished;
}
private enum MoveToType
{
Position,
Unit
}
[SerializeField] private MoveToType _moveTo;
[SerializeField] private BlackboardEntrySelector<Transform> _moveToPosition;
[SerializeField] private BlackboardEntrySelector<UnitObject> _moveToUnit;
[SerializeField, Space(15)] private BlackboardEntrySelector<float> _range;
public override void OnNodeEnter(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitMoveToMemory)nodeMemory;
var unitAgent = (UnitObjectSchemaAgent)agent;
memory.onMoveEnd = new SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory>(
unitAgent.unit.unitEvents.onMoveEnd, OnMoveEnd, unitAgent, memory);
memory.onMoveEnd.On();
memory.isMoving = false;
memory.isFinished = false;
}
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitMoveToMemory)nodeMemory;
var unitAgent = (UnitObjectSchemaAgent)agent;
// moveTo target not defined
if (_moveTo is MoveToType.Unit && !_moveToUnit.value) return NodeStatus.Failure;
if (_moveTo is MoveToType.Position && !_moveToPosition.value) return NodeStatus.Failure;
// unit reached target - end MoveTo with success
if (memory.isFinished) return NodeStatus.Success;
// not started moving yet - start moving for this unit
if (!memory.isMoving)
{
memory.isMoving = true;
memory.isFinished = false;
unitAgent.unit.OrderStop();
if (_moveTo is MoveToType.Position)
unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToPosition.value.position, _range.value));
if (_moveTo is MoveToType.Unit)
unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToUnit.value, _range.value));
}
// If somehow this unit don't have anything in current action (something aborted that??)
// Then end with failure whole MoveTo
if (unitAgent.unit.unitActionQueue.actionCurrent == null)
return NodeStatus.Failure;
// moving
return NodeStatus.Running;
}
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitMoveToMemory)nodeMemory;
memory.onMoveEnd.Off();
}
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitMoveToMemory)nodeMemory;
memory.onMoveEnd.Off();
}
private void OnMoveEnd(MoveEvent moveEvent, UnitMoveToMemory memory, UnitObjectSchemaAgent agent)
{
memory.isFinished = true;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 624124ec53d149399992419f68d2a199
timeCreated: 1709562199
@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Linq;
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Events;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("This unit will patrol between given points")]
[Category(SchemaCategory.RealtimeThisUnit)]
public class UnitPatrol : Action
{
private class UnitPatrolMemory
{
public SchemaUnitEventWrapper<MoveEvent, UnitPatrolMemory> onMoveEnd;
public int nextStop = - 1;
public bool isMoving = false;
}
public override void OnNodeEnter(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitPatrolMemory)nodeMemory;
var unitAgent = (UnitObjectSchemaAgent)agent;
var patrolPoints = unitAgent.unitAI.patrol;
var closestDistance = float.MaxValue;
for (var index = 0; index < patrolPoints.Count; index++)
{
var distance = Vector3.Distance(unitAgent.unit.transform.position, patrolPoints[index].position);
if (distance >= closestDistance) continue;
closestDistance = distance;
memory.nextStop = index;
}
memory.onMoveEnd = new SchemaUnitEventWrapper<MoveEvent, UnitPatrolMemory>(
unitAgent.unit.unitEvents.onMoveEnd, OnMoveEnd, unitAgent, memory);
memory.onMoveEnd.On();
}
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitPatrolMemory)nodeMemory;
var unitAgent = (UnitObjectSchemaAgent)agent;
if (memory.nextStop == - 1) return NodeStatus.Failure;
if (memory.isMoving) return NodeStatus.Running;
// Get next patrol points in order to check
var orderedPatrolPoints = new List<Transform>();
orderedPatrolPoints.AddRange(unitAgent.unitAI.patrol.Skip(memory.nextStop+1));
orderedPatrolPoints.AddRange(unitAgent.unitAI.patrol.Take(memory.nextStop+1));
// Find next valid patrol point and order move to it
foreach (var point in orderedPatrolPoints)
{
var moveAction = new MoveAction(unitAgent.unit, point.position);
if (moveAction.CanDoItBoolean() == false) continue;
unitAgent.unit.Order(moveAction);
memory.isMoving = true;
return NodeStatus.Running;
}
// Next point cant be found - error!
return NodeStatus.Failure;
}
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitPatrolMemory)nodeMemory;
memory.onMoveEnd.Off();
memory.isMoving = false;
}
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitPatrolMemory)nodeMemory;
memory.onMoveEnd.Off();
memory.isMoving = false;
}
private void OnMoveEnd(MoveEvent moveEvent, UnitPatrolMemory memory, UnitObjectSchemaAgent agent)
{
memory.nextStop = (memory.nextStop + 1) % agent.unitAI.patrol.Count;
memory.isMoving = false;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a11564bc03bb4a52a083e28d35d798cf
timeCreated: 1709223806
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Schema;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Turn;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Starts turn based fight")]
[Category(SchemaCategory.RealtimeThisUnit)]
public class UnitStartFight : Action
{
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var unitAgent = (UnitObjectSchemaAgent)agent;
var hostileUnits = unitAgent.unit.unitSenses.GetUnitsInSightByAttitude(AttitudeType.Hostile);
if (hostileUnits.Count == 0) return NodeStatus.Failure;
var units = new List<UnitObject>();
units.Add(unitAgent.unit);
units.AddRange(hostileUnits);
TurnManager.BuildGameObject(units);
return NodeStatus.Success;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 62ecce8528974c928665fff4143169dd
timeCreated: 1709379357
@@ -0,0 +1,110 @@
using System;
using System.Linq;
using Schema;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Events;
using VOID.ScriptableObjects.Abilities;
using Action = Schema.Action;
namespace VOID.Generic.AI.Schema.Actions
{
[Description("Order this unit to use selected ability")]
[Category(SchemaCategory.ThisUnit)]
public class UnitUseAbility : Action
{
private class UnitUseAbilityMemory
{
public SchemaUnitEventWrapper<AnimationGenericEvent, UnitUseAbilityMemory> onCastAnimationEnd;
public bool isRunning;
public bool isFinished;
}
private enum TargetType
{
Position,
Unit
}
[SerializeField] private BlackboardEntrySelector<BasicAbility> _ability;
[SerializeField, Space(15)] private TargetType _targetType;
[SerializeField] private BlackboardEntrySelector<Transform> _position;
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitUseAbilityMemory)nodeMemory;
var unitAgent = (UnitObjectSchemaAgent)agent;
var thisUnit = unitAgent.unit;
// Ability not selected
if (!_ability.value) return NodeStatus.Failure;
// Whenever is during casting or already done that
if (memory.isFinished) return NodeStatus.Success;
if (memory.isRunning) return NodeStatus.Running;
// Targets not selected
if (_targetType == TargetType.Position && !_position.value) return NodeStatus.Failure;
if (_targetType == TargetType.Unit && !_unit.value) return NodeStatus.Failure;
AbilityAction abilityAction;
if (_targetType is TargetType.Position)
{
// TODO: for now if ability needs multiple targets all will be identical
var targets = Enumerable.Repeat(_position.value.position, _ability.value.castUsages).ToArray();
abilityAction = new AbilityAction(thisUnit, _ability.value, targets);
}
else if (_targetType is TargetType.Unit)
{
// TODO: for now if ability needs multiple targets all will be identical
var targets = Enumerable.Repeat(_unit.value, _ability.value.castUsages).ToArray();
abilityAction = new AbilityAction(thisUnit, _ability.value, targets);
}
else
{
throw new ArgumentOutOfRangeException();
}
// Check if that action can be done
if (!abilityAction.CanDoItBoolean()) return NodeStatus.Failure;
// Listen to end of abilisty casting animation
memory.onCastAnimationEnd?.Off();
memory.onCastAnimationEnd = new SchemaUnitEventWrapper<AnimationGenericEvent, UnitUseAbilityMemory>(
thisUnit.unitEvents.onAnimation[AnimationType.Ability].onEnd, OnAbilityCastEnd, unitAgent, memory);
memory.onCastAnimationEnd.On();
// Everything seems ok - order unit to do that action
thisUnit.OrderStop();
thisUnit.Order(abilityAction);
memory.isRunning = true;
return NodeStatus.Running;
}
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitUseAbilityMemory)nodeMemory;
memory.onCastAnimationEnd?.Off();
memory.isFinished = false;
memory.isRunning = false;
}
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
{
var memory = (UnitUseAbilityMemory)nodeMemory;
memory.onCastAnimationEnd?.Off();
memory.isFinished = false;
memory.isRunning = false;
}
private void OnAbilityCastEnd(AnimationGenericEvent abilityCastEvent, UnitUseAbilityMemory memory,
UnitObjectSchemaAgent agent)
{
memory.isFinished = true;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45d528d62fdb41c7b539945d8d9f39be
timeCreated: 1709821286
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a75df9d1888e48babf6ec8f326d7d85a
timeCreated: 1709166099
@@ -0,0 +1,69 @@
using System;
using System.Text;
using Schema;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Util;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.AnyUnit)]
public class CanUnitMoveTo : Conditional
{
private enum MoveToType
{
Position,
Unit
}
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
[SerializeField, Space(15)] private MoveToType _moveTo;
[SerializeField] private BlackboardEntrySelector<Transform> _moveToPosition;
[SerializeField] private BlackboardEntrySelector<UnitObject> _moveToUnit;
[SerializeField, Space(15)] private BlackboardEntrySelector<float> _range;
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
var unitAgent = (UnitObjectSchemaAgent)agent;
switch (_moveTo)
{
case MoveToType.Position:
_unit.value.unitNavAgent.CalculatePathTo(_moveToPosition.value.position,
_range.value + unitAgent.unit.basicData.radius);
break;
case MoveToType.Unit:
_unit.value.unitNavAgent.CalculatePathTo(_moveToUnit.value.transform.position,
_range.value + unitAgent.unit.basicData.radius + _moveToUnit.value.basicData.radius);
break;
default:
throw new ArgumentOutOfRangeException();
}
var path = _unit.value.unitNavAgent.GetCorrectedPath();
// If somehow path isnt calculated - cant move
if (path.IsNullOrEmpty()) return false;
// Out of combat - costless move
if (!_unit.value.basicState.turnManager) return true;
// Calculate move cost when in turn combat
var cost = NavMeshPathUtil.CalculatePathCost(path);
return _unit.value.unitData.currentResources.movePoints >= cost;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append($"If <color=red>{_unit.name}</color> can move to ");
if (_moveTo is MoveToType.Position) sb.Append($"<color=red>{_moveToPosition.name}</color>");
if (_moveTo is MoveToType.Unit) sb.Append($"<color=red>{_moveToUnit.name}</color>");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3b68fb79d3b4f19bee514ad03229447
timeCreated: 1709732599
@@ -0,0 +1,89 @@
using System;
using System.Text;
using Schema;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.AnyUnit)]
public class IsBasicResource : Conditional
{
private enum ComparisonType
{
Greater,
Less
}
private enum ValueType
{
Value,
Percent
}
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
[SerializeField, Space(15)] private BasicResourceType _resourceType;
[SerializeField] private ComparisonType _comparisonType;
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _value = new(1);
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Percent)] private BlackboardEntrySelector<int> _percent = new(50);
[SerializeField] private ValueType _compareBy;
private void OnValidate()
{
_percent.inspectorValue = Mathf.Min(Mathf.Max(_percent.inspectorValue, 0), 100);
_value.inspectorValue = Mathf.Max(_value.inspectorValue, 0);
}
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return _compareBy switch
{
ValueType.Percent => CompareByPercent(),
ValueType.Value => CompareByValue(),
_ => throw new ArgumentOutOfRangeException()
};
}
private bool CompareByValue()
{
var currentResource = _unit.value.basicData.currentResources.Get(_resourceType);
return _comparisonType switch
{
ComparisonType.Greater => currentResource > _value.value,
ComparisonType.Less => currentResource < _value.value,
_ => throw new ArgumentOutOfRangeException()
};
}
private bool CompareByPercent()
{
var currentResource = _unit.value.basicData.currentResources.Get(_resourceType);
var maxResource = _unit.value.basicData.maxResources.Get(_resourceType);
var currentPercent = currentResource / maxResource * 100;
return _comparisonType switch
{
ComparisonType.Greater => currentPercent > _percent.value,
ComparisonType.Less => currentPercent < _percent.value,
_ => throw new ArgumentOutOfRangeException()
};
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append(
$"If <color=red>{_unit.name}</color> {nameof(UnitObjectData)}'s <color=red>{_resourceType}</color>");
sb.Append(_comparisonType == ComparisonType.Greater ? " > " : " < ");
if (_compareBy is ValueType.Percent) sb.Append($"<color=red>{_percent.name}</color>%");
if (_compareBy is ValueType.Value) sb.Append($"<color=red>{_value.name}</color>");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1245ec365cb943c6acc3d9e85330d07a
timeCreated: 1709501957
@@ -0,0 +1,26 @@
using System.Text;
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.ThisUnit)]
public class IsPatrolDefined : Conditional
{
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return (agent as UnitObjectSchemaAgent)?.unitAI.patrol.Count > 1;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append($"<color=red>{nameof(UnitObjectSenses)}</color> has at least two <color=red>patrol</color> points");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 74564f3f9d4b467bb3e954c5a84cf780
timeCreated: 1709383094
@@ -0,0 +1,28 @@
using System.Text;
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.AnyUnit)]
public class IsUnitDead : Conditional
{
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return _unit.value && _unit.value.basicState.isDead;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append($"If <color=red>{_unit.name}</color> is dead");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba8914a1869a4e06aebdeebf2ec743cd
timeCreated: 1710974135
@@ -0,0 +1,30 @@
using System.Text;
using Schema;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.ThisUnit)]
public class IsUnitInSight : Conditional
{
[SerializeField] private AttitudeType _attitudeType;
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return (agent as UnitObjectSchemaAgent)?.unit.unitSenses.GetUnitsInSightByAttitude(_attitudeType).Count > 0;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append(
$"<color=red>{nameof(UnitObjectSenses)}</color> has at least one <color=red>{_attitudeType}</color> in range");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6eed700ee23b489189517c4ae32e687a
timeCreated: 1709382718
@@ -0,0 +1,26 @@
using System.Text;
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.ThisUnit)]
public class IsUnitObjectSchema : Conditional
{
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return agent is UnitObjectSchemaAgent unitAgent && unitAgent.unit;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append($"If this is <color=red>{nameof(UnitObject)}</color>'s schema");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2b727768fcf6433687f118b54fe16e53
timeCreated: 1709203631
@@ -0,0 +1,89 @@
using System;
using System.Text;
using Schema;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.AnyUnit)]
public class IsUnitResource : Conditional
{
private enum ComparisonType
{
Greater,
Less
}
private enum ValueType
{
Value,
Percent
}
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
[SerializeField, Space(15)] private UnitResourceType _resourceType;
[SerializeField] private ComparisonType _comparisonType;
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _value = new(1);
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _percent = new(50);
[SerializeField] private ValueType _compareBy;
private void OnValidate()
{
_percent.inspectorValue = Mathf.Min(Mathf.Max(_percent.inspectorValue, 0), 100);
_value.inspectorValue = Mathf.Max(_value.inspectorValue, 0);
}
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return _compareBy switch
{
ValueType.Percent => CompareByPercent(),
ValueType.Value => CompareByValue(),
_ => throw new ArgumentOutOfRangeException()
};
}
private bool CompareByValue()
{
var currentResource = _unit.value.unitData.currentResources.Get(_resourceType);
return _comparisonType switch
{
ComparisonType.Greater => currentResource > _value.value,
ComparisonType.Less => currentResource < _value.value,
_ => throw new ArgumentOutOfRangeException()
};
}
private bool CompareByPercent()
{
var currentResource = _unit.value.unitData.currentResources.Get(_resourceType);
var maxResource = _unit.value.unitData.maxResources.Get(_resourceType);
var currentPercent = currentResource / maxResource * 100;
return _comparisonType switch
{
ComparisonType.Greater => currentPercent > _percent.value,
ComparisonType.Less => currentPercent < _percent.value,
_ => throw new ArgumentOutOfRangeException()
};
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append(
$"If <color=red>{_unit.name}</color> {nameof(UnitObjectData)}'s <color=red>{_resourceType}</color>");
sb.Append(_comparisonType == ComparisonType.Greater ? " > " : " < ");
if (_compareBy is ValueType.Percent) sb.Append($"<color=red>{_percent.name}</color>%");
if (_compareBy is ValueType.Value) sb.Append($"<color=red>{_value.name}</color>");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9851bd79142742a4a00ffe81f4cf8385
timeCreated: 1709492242
@@ -0,0 +1,28 @@
using System.Text;
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Conditionals
{
[DarkIcon("Conditionals/d_IsNull")]
[LightIcon("Conditionals/IsNull")]
[Category(SchemaCategory.CombatAnyUnit)]
public class IsUnitTurn : Conditional
{
[SerializeField] public BlackboardEntrySelector<UnitObject> _unit;
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
{
return _unit.value && _unit.value.basicState.turnManager && _unit.value.unitState.isSelfTurn;
}
public override GUIContent GetConditionalContent()
{
var sb = new StringBuilder();
if (invert) sb.Append("<color=red>NOT</color> ");
sb.Append($"If its <color=red>{_unit.name}</color>'s turn");
return new GUIContent(sb.ToString());
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: da0d9e0133ee4779bc1daea1bb680d25
timeCreated: 1709565723
@@ -0,0 +1,13 @@
namespace VOID.Generic.AI.Schema
{
public static class SchemaCategory
{
private const string VoidCategory = "VOID RPG / ";
public const string CombatAnyUnit = VoidCategory + "Combat only / Any Unit";
public const string RealtimeAnyUnit = VoidCategory + "Realtime only / Any Unit";
public const string CombatThisUnit = VoidCategory + "Combat only / This Unit";
public const string RealtimeThisUnit = VoidCategory + "Realtime only / This Unit";
public const string AnyUnit = VoidCategory + "Any Unit";
public const string ThisUnit = VoidCategory + "This Unit";
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 33bca9a6c7ab4879a2e5a29f53b78b41
timeCreated: 1709820395
@@ -0,0 +1,31 @@
using UnityEngine.Events;
using VOID.Generic.Objects.Abstract.Events;
namespace VOID.Generic.AI.Schema
{
public class SchemaUnitEventWrapper<TUnitEvent, TMemory> where TUnitEvent : AbstractEvent
{
private UnityEvent<TUnitEvent> _listener;
private UnityAction<TUnitEvent> _action;
public SchemaUnitEventWrapper(
UnityEvent<TUnitEvent> addListenerTo,
UnityAction<TUnitEvent, TMemory, UnitObjectSchemaAgent> action,
UnitObjectSchemaAgent agent,
TMemory memory)
{
_listener = addListenerTo;
_action = t1 => action.Invoke(t1, memory, agent);
}
public void On()
{
_listener.AddListener(_action);
}
public void Off()
{
_listener.RemoveListener(_action);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 80f30b03c1f0439fbefcc92e468b592b
timeCreated: 1709228750
@@ -0,0 +1,11 @@
using Schema;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema
{
public class UnitObjectSchemaAgent : SchemaAgent
{
public UnitObject unit;
public UnitAI unitAI;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6adfb8fe7d19483aa20b5a2d03709433
timeCreated: 1709501103
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2b2f0e2aece4ae684c4e3e19189ef70
timeCreated: 1709163584
@@ -0,0 +1,19 @@
using Schema;
using VOID.ScriptableObjects.Abilities;
namespace VOID.Generic.AI.Schema.Variables
{
[Color("#b07219")]
[Name(nameof(BasicAbility))]
[UseExternalTypeDefinition(typeof(BasicAbility))]
[IncludePaths(
"actionPointCost",
"movePointCost",
"isRanged",
"range",
"castUsages"
)]
public class AbilityVariable : EntryType
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e9715994280f4de0b20ae8cc047a02e3
timeCreated: 1709730991
@@ -0,0 +1,30 @@
using Schema;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
namespace VOID.Generic.AI.Schema.Variables
{
[Color("#b07219")]
[Name(nameof(AbstractObject))]
[UseExternalTypeDefinition(typeof(AbstractObject))]
[ExcludePaths(
"gameObject",
"transform",
"transform.position",
"basicData.currentResources.health",
"basicData.resistances.physicalResistance",
"basicData.resistances.magicalResistance",
"basicData.resistances.fireResistance",
"basicData.resistances.waterResistance",
"basicData.resistances.earthResistance",
"basicData.resistances.airResistance",
"basicData.resistances.poisonResistance",
"basicData.resistances.lightResistance",
"basicData.resistances.darkResistance",
"basicData.resistances.healResistance"
)]
[ExcludeTypes(typeof(Matrix4x4))]
public class AbstractObjectVariable : EntryType
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d4c4f14007c8494fa0d8743dffa15a98
timeCreated: 1709564147
@@ -0,0 +1,30 @@
using Schema;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.AI.Schema.Variables
{
[Color("#b07219")]
[Name(nameof(UnitObject))]
[UseExternalTypeDefinition(typeof(UnitObject))]
[IncludePaths(
"gameObject",
"transform",
"transform.position",
"basicData.currentResources.health",
"basicData.resistances.physicalResistance",
"basicData.resistances.magicalResistance",
"basicData.resistances.fireResistance",
"basicData.resistances.waterResistance",
"basicData.resistances.earthResistance",
"basicData.resistances.airResistance",
"basicData.resistances.poisonResistance",
"basicData.resistances.lightResistance",
"basicData.resistances.darkResistance",
"basicData.resistances.healResistance",
"unitData.level",
"unitData.exp"
)]
public class UnitObjectVariable : EntryType
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8a55139c845b4f4284fc7d276794d548
timeCreated: 1709163608