init
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Sirenix.Utilities;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Dict.Ability;
|
||||
using VOID.Generic.Objects.Abstract;
|
||||
using VOID.Generic.Objects.Unit.Actions.Exceptions;
|
||||
using VOID.Generic.Objects.Unit.Events;
|
||||
using VOID.Generic.Projectile;
|
||||
using VOID.Generic.Util;
|
||||
using VOID.ScriptableObjects;
|
||||
using VOID.ScriptableObjects.Abilities;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace VOID.Generic.Objects.Unit.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes <see cref="UnitObject"/> cast given <see cref="BasicAbility"/>.
|
||||
/// <br/><br/>
|
||||
/// <see cref="AbstractAction"/>:<br/>
|
||||
/// <inheritdoc cref="AbstractAction"/>
|
||||
/// </summary>
|
||||
public class AbilityAction : AbstractAction
|
||||
{
|
||||
private class Target
|
||||
{
|
||||
public readonly AbstractObject targetObject;
|
||||
public readonly Vector3 targetPosition;
|
||||
|
||||
public Target(AbstractObject targetObject, Vector3 targetPosition)
|
||||
{
|
||||
this.targetObject = targetObject;
|
||||
this.targetPosition = targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly UnitObject _unit;
|
||||
private readonly BasicAbility _ability;
|
||||
private readonly Target[] _targets;
|
||||
|
||||
private int _projectilesEnded;
|
||||
private AbilityCastEvent _abilityEvent;
|
||||
|
||||
public AbilityAction(UnitObject unit, BasicAbility ability)
|
||||
: this(unit, ability, Array.Empty<Target>())
|
||||
{
|
||||
}
|
||||
|
||||
public AbilityAction(UnitObject unit, BasicAbility ability, Vector3 target)
|
||||
: this(unit, ability, new[] { new Target(null, target) })
|
||||
{
|
||||
}
|
||||
|
||||
public AbilityAction(UnitObject unit, BasicAbility ability, AbstractObject target)
|
||||
: this(unit, ability, new[] { new Target(target, target.transform.position + Vector3.up * target.basicData.height/2) })
|
||||
{
|
||||
}
|
||||
|
||||
private AbilityAction(UnitObject unit, BasicAbility ability, Target target)
|
||||
: this(unit, ability, new[] { target })
|
||||
{
|
||||
}
|
||||
|
||||
public AbilityAction(UnitObject unit, BasicAbility ability, IEnumerable<Vector3> targets)
|
||||
: this(unit, ability, targets.Select(p => new Target(null, p)).ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
public AbilityAction(UnitObject unit, BasicAbility ability, IEnumerable<AbstractObject> targets)
|
||||
: this(unit, ability, targets.Select(o => new Target(o, o.transform.position+ Vector3.up * o.basicData.height/2)).ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
private AbilityAction(UnitObject unit, BasicAbility ability, Target[] targets)
|
||||
{
|
||||
_unit = unit;
|
||||
_ability = ability;
|
||||
_targets = targets;
|
||||
}
|
||||
|
||||
public override bool IsInRange()
|
||||
{
|
||||
return _targets.IsNullOrEmpty() || _targets.All(target =>
|
||||
target.targetObject
|
||||
? RangeUtil.IsInRange(_unit, target.targetObject, _ability.range)
|
||||
: RangeUtil.IsInRange(_unit, target.targetPosition, _ability.range));
|
||||
}
|
||||
|
||||
public override bool CanBeStopped() => _abilityEvent is null || _abilityEvent.prevented;
|
||||
|
||||
public override void CanDoIt()
|
||||
{
|
||||
Check(
|
||||
state == ActionState.Ready,
|
||||
UnitActionErrorType.ActionStateIsNotReady);
|
||||
if (_unit.basicState.turnManager)
|
||||
{
|
||||
Check(
|
||||
_unit.unitState.isSelfTurn,
|
||||
UnitActionErrorType.UnitSelfTurnNotActive);
|
||||
Check(
|
||||
_unit.unitData.currentResources.actionPoints >= _ability.actionPointCost,
|
||||
UnitActionErrorType.UnitNotEnoughActionPoints);
|
||||
Check(
|
||||
_unit.unitData.currentResources.movePoints >= _ability.movePointCost,
|
||||
UnitActionErrorType.UnitNotEnoughMovePoints);
|
||||
}
|
||||
|
||||
Check(
|
||||
_ability is not BasicAttackAbility || _unit.unitData.canAttack,
|
||||
UnitActionErrorType.ThisUnitCantAttack);
|
||||
Check(
|
||||
_unit.unitState.isControllable,
|
||||
UnitActionErrorType.UnitStateIsNotControllable);
|
||||
Check(
|
||||
IsInRange(),
|
||||
UnitActionErrorType.NotInRange);
|
||||
Check(
|
||||
_ability.requirements.All(requirement => requirement.Check(_unit)),
|
||||
UnitActionErrorType.RequirementsNotMeetToUseThatAbility);
|
||||
Check(
|
||||
_ability.projectileSteps.First().validTargets.All(validTarget =>
|
||||
_targets.All(target => validTarget.IsValid(_unit, target.targetPosition, target.targetObject))),
|
||||
UnitActionErrorType.WrongTargetsSelectedToUseThatAbility);
|
||||
Check(
|
||||
_ability.aimingType is AbilityAimingType.Blank || _ability.castUsages == _targets.Length,
|
||||
UnitActionErrorType.WrongNumberOfTargetsToUseThatAbility);
|
||||
}
|
||||
|
||||
protected override void OnDoIt()
|
||||
{
|
||||
if (_unit.basicState.turnManager)
|
||||
{
|
||||
_unit.unitData.UseResource(UnitResourceType.ActionPoints, _ability.actionPointCost);
|
||||
_unit.unitData.UseResource(UnitResourceType.MovePoints, _ability.movePointCost);
|
||||
}
|
||||
|
||||
_abilityEvent = new AbilityCastEvent
|
||||
{
|
||||
caster = _unit,
|
||||
ability = _ability
|
||||
};
|
||||
|
||||
_unit.unitEvents.onStartAbilityBefore.Invoke(_abilityEvent);
|
||||
|
||||
Check(!_abilityEvent.prevented, UnitActionErrorType.AbilityIsPrevented);
|
||||
|
||||
_unit.unitEvents.onStartAbilityAfter.Invoke(_abilityEvent);
|
||||
|
||||
if (_ability.castingType == AbilityCastingType.Instant)
|
||||
{
|
||||
DoAbility(null);
|
||||
EndIt();
|
||||
return;
|
||||
}
|
||||
|
||||
var animationGenericEvent = new AnimationGenericEvent
|
||||
{
|
||||
animationType = AnimationType.Ability,
|
||||
unit = _unit,
|
||||
ability = _ability
|
||||
};
|
||||
|
||||
if (_targets.Length > 0) new RotateAction(_unit, _targets[0].targetPosition).DoIt();
|
||||
|
||||
if (_ability.castingType is AbilityCastingType.Channel && _ability.castUsages > 0)
|
||||
{
|
||||
// When channeling - ability ends when all projectiles end
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onPerform.AddListener(DoAbility);
|
||||
_unit.unitAnimator.SetLoopAnimation(AnimationType.Ability, _ability.abilityLoopAnimationList);
|
||||
_unit.unitAnimator.StartLoopAnimation(animationGenericEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise - ability ends with animation
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onPerform.AddListener(DoAbility);
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onEnd.AddListener(OnAbilityEnd);
|
||||
_unit.unitAnimator.SetAnimation(AnimationType.Ability, _ability.abilityAnimationList);
|
||||
_unit.unitAnimator.StartAnimation(animationGenericEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoAbility(AnimationGenericEvent tEvent)
|
||||
{
|
||||
BeforePerform();
|
||||
if (_ability.applyOnCasterMoment is AbilityExecutionMoment.AfterCast) ApplyEffectsToCaster();
|
||||
_targets.ForEach((target, index) =>
|
||||
{
|
||||
// Predefined object is only for visual things
|
||||
// Else - new empty projectile object
|
||||
var projectile = _ability.projectile
|
||||
? Object.Instantiate(_ability.projectile.gameObject).GetComponent<ProjectileObject>()
|
||||
: new GameObject().AddComponent<ProjectileObject>();
|
||||
|
||||
projectile.StartProjectile(index, _unit, _unit.unitData.castPosition.position, target.targetPosition, _ability.projectileSteps);
|
||||
projectile.onHit.AddListener(OnProjectileHit);
|
||||
projectile.onEnd.AddListener(OnProjectileEnd);
|
||||
|
||||
_unit.unitEvents.onAbilityProjectileStart.Invoke(new AbilityProjectileEvent
|
||||
{
|
||||
caster = _unit,
|
||||
ability = _ability,
|
||||
projectileIndex = index,
|
||||
projectile = projectile
|
||||
});
|
||||
});
|
||||
AfterPerform();
|
||||
}
|
||||
|
||||
private void OnProjectileHit(ProjectileObject projectile, AbstractObject hitObject)
|
||||
{
|
||||
var abilityHitEvent = new AbilityHitEvent
|
||||
{
|
||||
caster = _unit,
|
||||
target = hitObject,
|
||||
ability = _ability
|
||||
};
|
||||
|
||||
hitObject.basicEvents.onAbilityHitBefore.Invoke(abilityHitEvent);
|
||||
_unit.unitEvents.onAbilityHitBefore.Invoke(abilityHitEvent);
|
||||
|
||||
// We cant run Check here, because this OnHit can be one of many - just do nothing more for this projectile
|
||||
if (abilityHitEvent.prevented) return;
|
||||
|
||||
hitObject.basicEvents.onAbilityHitAfter.Invoke(abilityHitEvent);
|
||||
_unit.unitEvents.onAbilityHitAfter.Invoke(abilityHitEvent);
|
||||
|
||||
ApplyEffectsToTarget(hitObject);
|
||||
}
|
||||
|
||||
private void OnProjectileEnd(ProjectileObject projectile)
|
||||
{
|
||||
_projectilesEnded++;
|
||||
|
||||
_unit.unitEvents.onAbilityProjectileEnd.Invoke(new AbilityProjectileEvent
|
||||
{
|
||||
caster = _unit,
|
||||
ability = _ability,
|
||||
projectileIndex = projectile.index,
|
||||
projectile = projectile
|
||||
});
|
||||
|
||||
if (_ability.castingType is AbilityCastingType.Channel && _projectilesEnded == _ability.castUsages)
|
||||
{
|
||||
_unit.unitAnimator.EndLoopAnimation();
|
||||
EndIt();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyEffectsToTarget(AbstractObject target)
|
||||
{
|
||||
if (_ability.applyOnTarget.Count <= 0) return;
|
||||
|
||||
// Because ability have defined only effects we need create dummy status
|
||||
// IMPORTANT - It is hidden and instantly starts and then ends!
|
||||
var targetStatus = ScriptableObject.CreateInstance<BasicStatus>();
|
||||
targetStatus.effects = _ability.applyOnTarget;
|
||||
targetStatus.hidden = true;
|
||||
target.statusManager.AddStatus(targetStatus, _unit, false);
|
||||
}
|
||||
|
||||
private void ApplyEffectsToCaster()
|
||||
{
|
||||
if (_ability.applyOnCaster.Count <= 0) return;
|
||||
|
||||
// Because ability have defined only effects we need create dummy status
|
||||
// IMPORTANT - It is hidden and instantly starts and then ends!
|
||||
var casterStatus = ScriptableObject.CreateInstance<BasicStatus>();
|
||||
casterStatus.effects = _ability.applyOnCaster;
|
||||
casterStatus.hidden = true;
|
||||
_unit.statusManager.AddStatus(casterStatus, _unit, false);
|
||||
}
|
||||
|
||||
private void OnAbilityEnd(AnimationGenericEvent tEvent)
|
||||
{
|
||||
EndIt();
|
||||
}
|
||||
|
||||
protected override void OnCancelIt()
|
||||
{
|
||||
_unit.unitAnimator.EndLoopAnimation();
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onPerform.RemoveListener(DoAbility);
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onEnd.RemoveListener(OnAbilityEnd);
|
||||
_unit.unitEvents.onEndAbility.Invoke(_abilityEvent);
|
||||
}
|
||||
|
||||
protected override void OnEndIt()
|
||||
{
|
||||
_unit.unitAnimator.EndLoopAnimation();
|
||||
if (_ability.applyOnCasterMoment is AbilityExecutionMoment.AfterEnd) ApplyEffectsToCaster();
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onPerform.RemoveListener(DoAbility);
|
||||
_unit.unitEvents.onAnimation[AnimationType.Ability].onEnd.RemoveListener(OnAbilityEnd);
|
||||
_unit.unitEvents.onEndAbility.Invoke(_abilityEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user