959e80cf72
assets upload description.
1333 lines
46 KiB
C#
1333 lines
46 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using RPGCreationKit;
|
|
using RPGCreationKit.BehaviourTree;
|
|
using System.Linq;
|
|
using RPGCreationKit.Player;
|
|
|
|
namespace RPGCreationKit.AI
|
|
{
|
|
/// <summary>
|
|
/// Adds the Combat System to the AI.
|
|
/// </summary>
|
|
public class AICombatSystem : AIPerception, IMeleeAttacker, IRangedAttacker
|
|
{
|
|
[Header("References")]
|
|
public RuntimeAnimatorController defaultAnimatorController; // The RuntimeAnimatorController that needs to be played when the entity is not in combat
|
|
|
|
public WeaponItem defaultWeapon; // This is the weapon that will be equipped if there's none (usually it is fists)
|
|
public WeaponOnHand defaultWeaponOnHand;
|
|
public SpellsKnowledge spellsKnowledge;
|
|
|
|
public GameObject blockingArea;
|
|
|
|
public int YNewProperty
|
|
{ get; set; }
|
|
|
|
public float CurrentHealth
|
|
{
|
|
get
|
|
{
|
|
return attributes.CurHealth;
|
|
}
|
|
}
|
|
|
|
public Projectile currentProjectile;
|
|
public AudioSource combatAudioSource;
|
|
|
|
// Spells Related
|
|
GameObject spellProjectile;
|
|
SpellProjectile currentSpellProjectile;
|
|
|
|
[Space(10)]
|
|
[Header("Status")]
|
|
public float targetDistance2D = 99999.9f;
|
|
|
|
public bool weaponDrawn = false;
|
|
public bool canAttack = true;
|
|
public bool isAttacking = false; // Is playing attack animation
|
|
public bool isCastingSpell = false;
|
|
public bool spellsLimitedByMana = false;
|
|
public bool isBlocking = false;
|
|
public bool isUnbalanced = false;
|
|
|
|
public bool lastAttackWasCharged;
|
|
public int curAttackType = 0;
|
|
public int curChargedAttackType = 0;
|
|
[HideInInspector] public int lastAttackIndex = 0;
|
|
[HideInInspector] public int lastChargedAttackIndex = 0;
|
|
|
|
bool wantsToAttack;
|
|
|
|
public bool hasToBlock = false;
|
|
|
|
public int combatType = 0;
|
|
public bool helpsMembersOfSameFactions = true;
|
|
|
|
|
|
public EntityAttributes mainTargetAttributes;
|
|
|
|
// BehaviourTrees
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
if (isAlive && !isInOfflineMode)
|
|
{
|
|
BlockCheck();
|
|
|
|
CheckForEnemies();
|
|
|
|
if (m_isInCombat)
|
|
WhileInCombat();
|
|
|
|
}
|
|
else if(!isAlive)
|
|
{
|
|
// Force health to be negative if dead
|
|
attributes.CurHealth = -1;
|
|
}
|
|
}
|
|
|
|
// Use this for initialization
|
|
public override void Start()
|
|
{
|
|
YNewProperty = 10000;
|
|
|
|
base.Start();
|
|
|
|
if (!equipment.currentWeapon && defaultWeapon)
|
|
{
|
|
equipment.currentWeapon = defaultWeapon;
|
|
currentWeaponOnHand = defaultWeaponOnHand;
|
|
defaultWeaponOnHand.gameObject.SetActive(true);
|
|
equipment.currentWeaponObject = null;
|
|
|
|
canAttack = true;
|
|
|
|
if (weaponDrawn)
|
|
{
|
|
m_Anim.runtimeAnimatorController = equipment.currentWeapon.tpsAnimatorController;
|
|
m_Anim.SetTrigger("Draw");
|
|
}
|
|
|
|
m_Anim.SetBool("isDrawn", weaponDrawn);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
[ContextMenu("Draw Weapon"), AIInvokable]
|
|
public void DrawWeapon()
|
|
{
|
|
combatType = (int)equipment.currentWeapon.weaponType;
|
|
m_Anim.SetInteger("CombatType", combatType);
|
|
|
|
//m_Anim.runtimeAnimatorController = currentWeapon.tpsAnimatorController;
|
|
|
|
m_Anim.SetTrigger("Draw");
|
|
m_Anim.SetBool("isDrawn", true);
|
|
|
|
//FullComboChainReset();
|
|
wantsToAttack = false;
|
|
isDrawingWeapon = true;
|
|
|
|
weaponDrawn = true;
|
|
}
|
|
|
|
[ContextMenu("Undraw Weapon")]
|
|
public void UndrawWeapon()
|
|
{
|
|
if (weaponDrawn == false || !isAlive)
|
|
return;
|
|
|
|
combatType = (int)equipment.currentWeapon.weaponType;
|
|
m_Anim.SetInteger("CombatType", combatType);
|
|
|
|
weaponDrawn = false;
|
|
m_Anim.SetBool("isDrawn", false);
|
|
m_Anim.SetTrigger("Undraw");
|
|
|
|
wantsToAttack = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by animation event, resets the animator runtime controller to default
|
|
/// </summary>
|
|
public void UndrawWeaponEvent()
|
|
{
|
|
//m_Anim.runtimeAnimatorController = defaultAnimatorController;
|
|
isInCombat = false;
|
|
m_Anim.SetBool("InCombat", false);
|
|
FullComboChainReset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by animation event, resets the animator runtime controller to default
|
|
/// </summary>
|
|
public void DrawWeaponEvent()
|
|
{
|
|
//m_Anim.runtimeAnimatorController = defaultAnimatorController;
|
|
isInCombat = true;
|
|
m_Anim.SetBool("InCombat", true);
|
|
FullComboChainReset();
|
|
|
|
isSwitchingWeapon = false;
|
|
isDrawingWeapon = false;
|
|
}
|
|
|
|
public bool m_isInCombat = false;
|
|
|
|
public void EnterInCombatAgainst(Entity _entiy)
|
|
{
|
|
if (m_isInCombat || !isAlive)
|
|
return;
|
|
|
|
enterInCombatCalled = true;
|
|
StartCoroutine(EnterInCombatTask(_entiy));
|
|
}
|
|
|
|
bool doonce = false;
|
|
public IEnumerator EnterInCombatTask(Entity _entiy)
|
|
{
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
if (isUsingActionPoint)
|
|
{
|
|
if (!doonce)
|
|
{
|
|
shouldUseNPCActionPoint = false;
|
|
isReachingActionPoint = false;
|
|
StopUsingNPCActionPoint();
|
|
doonce = true;
|
|
}
|
|
|
|
while (isUsingActionPoint)
|
|
{
|
|
yield return null;
|
|
}
|
|
agent.enabled = true;
|
|
}
|
|
|
|
if (isInConversation)
|
|
while (isInConversation)
|
|
yield return null;
|
|
|
|
|
|
doonce = false;
|
|
// Movements
|
|
StopFollowingPath();
|
|
|
|
ResetActionPointAgentState();
|
|
|
|
// Stop following the current NavMeshPath
|
|
if (navmeshPath == null)
|
|
navmeshPath = new UnityEngine.AI.NavMeshPath();
|
|
|
|
if (agent.isActiveAndEnabled)
|
|
agent.CalculatePath(agent.transform.position + (transform.forward * 0.1f), navmeshPath);
|
|
|
|
// Set the main target
|
|
SetTarget(_entiy.gameObject);
|
|
|
|
// Try to get the target attributes
|
|
mainTargetAttributes = mainTarget.GetComponent<EntityAttributes>();
|
|
|
|
enemyTargets.Add(new VisibleEnemy(_entiy, mainTargetAttributes, new AggroInfo(50)));
|
|
|
|
m_isInCombat = true;
|
|
OnEnterInCombat();
|
|
|
|
yield break;
|
|
}
|
|
|
|
/// TO ALLOW OVERRIDE THE TASK OR AT LEAST RUN CODE AT THE END
|
|
public virtual void OnEnterInCombat()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the Entity given is an Enemy Target
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool IsFightingEntity(Entity _entity)
|
|
{
|
|
for (int i = 0; i < enemyTargets.Count; i++)
|
|
if (enemyTargets[i].m_entity == _entity)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public int SortByAggro(VisibleEnemy a1, VisibleEnemy a2)
|
|
{
|
|
return a1.m_aggro.aggroValue.CompareTo(a2.m_aggro.aggroValue);
|
|
}
|
|
|
|
public virtual void LeaveCombat()
|
|
{
|
|
if (!isAlive)
|
|
return;
|
|
|
|
ClearTarget();
|
|
mainTargetAttributes = null;
|
|
SetLookAtTarget(false);
|
|
hasToBlock = false;
|
|
|
|
//if (setMainTarget)
|
|
// mainTarget = setMainTarget;
|
|
|
|
// Stop following the current NavMeshPath
|
|
try
|
|
{
|
|
if(agent.isOnNavMesh)
|
|
agent.CalculatePath(agent.transform.position + (transform.forward * 0.1f), navmeshPath);
|
|
}
|
|
catch
|
|
{
|
|
|
|
}
|
|
|
|
m_isInCombat = false;
|
|
enterInCombatCalled = false;
|
|
// Undraw
|
|
UndrawWeapon();
|
|
|
|
// Default is seek
|
|
selectedSteeringBehaviour = SteeringBehaviours.Seek;
|
|
|
|
}
|
|
|
|
public float curCombatPulse = 999999; // initialize at this value to have an instant pulse
|
|
public void WhileInCombat()
|
|
{
|
|
if (!m_isInCombat)
|
|
return;
|
|
|
|
curCombatPulse += AggroSettings.COMBAT_PULSE_RATE * Time.deltaTime;
|
|
if (curCombatPulse >= AggroSettings.COMBAT_PULSE_RATE)
|
|
{
|
|
// Update aggro pulse and list
|
|
for (int i = 0; i < enemyTargets.Count; i++)
|
|
{
|
|
if (enemyTargets[i].m_entityAttributes.CurHealth <= 0)
|
|
{
|
|
enemyTargets.RemoveAt(i);
|
|
continue;
|
|
}
|
|
|
|
enemyTargets[i].m_aggro.CombatPulse();
|
|
enemyTargets[i].m_aggro.AlterAggroValue(AggroSettings.Modifier.Distance, Vector3.Distance(transform.position, enemyTargets[i].m_entity.transform.position));
|
|
}
|
|
|
|
if(enemyTargets.Count > 0)
|
|
enemyTargets = enemyTargets.OrderByDescending(a => a.m_aggro.aggroValue).ToList();
|
|
|
|
curCombatPulse = 0;
|
|
|
|
// Always set the most threating enemy as mainTarget
|
|
if (enemyTargets.Count > 0)
|
|
{
|
|
SetTarget(enemyTargets[0].m_entity.gameObject);
|
|
|
|
if (aiLookAt != null && enemyTargets[0].m_entity.entityFocusPart != null)
|
|
aiLookAt.ForceToLookAtTarget(enemyTargets[0].m_entity.entityFocusPart.transform);
|
|
|
|
mainTargetAttributes = enemyTargets[0].m_entityAttributes;
|
|
}
|
|
else
|
|
{
|
|
if (aiLookAt != null)
|
|
aiLookAt.StopForcingLookAtTarget();
|
|
|
|
LeaveCombat();
|
|
}
|
|
}
|
|
|
|
// if is using bow and is standing still always face target
|
|
if(currentWeaponOnHand.weaponItem.weaponType == WeaponType.Bow || currentWeaponOnHand.weaponItem.weaponType == WeaponType.Crossbow)
|
|
{
|
|
if(isStopped && mainTarget != null)
|
|
{
|
|
// Rotate torwards target
|
|
var lookAt = Quaternion.LookRotation(mainTarget.position - transform.position);
|
|
transform.rotation = Quaternion.Slerp(transform.rotation, lookAt, Time.deltaTime * generalRotationSpeed);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CheckForEnemies()
|
|
{
|
|
if (!isLoaded || isInConversation) // if this is not loaded skip until it is
|
|
return;
|
|
|
|
for(int i = 0; i < visibleAI.Count; i++)
|
|
{
|
|
if (visibleAI[i] == null || !visibleAI[i].isLoaded) // If the entity is not loaded we have no way to tell if he's dead or not
|
|
continue;
|
|
|
|
if (visibleAI[i].isAlive && !enemyTargets.Any(t => t.m_entity == visibleAI[i]) && Faction.AreHostile(this.belongsToFactions, visibleAI[i].belongsToFactions))
|
|
{
|
|
if (!m_isInCombat)
|
|
EnterInCombatAgainst(visibleAI[i]);
|
|
|
|
enemyTargets.Add(new VisibleEnemy(visibleAI[i], visibleAI[i].GetComponent<EntityAttributes>(), new AggroInfo(50)));
|
|
}
|
|
else if (!m_isInCombat && enemyTargets.Any(t => t.m_entity == visibleAI[i]))
|
|
EnterInCombatAgainst(visibleAI[i]);
|
|
|
|
// check if we're not in combat and if a memeber of our faction is fighting against an enemy
|
|
if(helpsMembersOfSameFactions)
|
|
{
|
|
if(visibleAI[i].isInCombat && !isInCombat &&
|
|
visibleAI[i].belongsToFactions.Any(t => belongsToFactions.Contains(FactionsDatabase.GetFaction(t.ID))))
|
|
{
|
|
if (visibleAI[i].enemyTargets.Count > 1)
|
|
{
|
|
EnterInCombatAgainst(visibleAI[i].enemyTargets[0].m_entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!m_isInCombat && enemyTargets.Any(t => t.m_entity.CompareTag("Player")) && visibleTargets.Any(t => t.tr.CompareTag("Player"))
|
|
|| visibleTargets.Any(t => t.tr.CompareTag("Player") && !enemyTargets.Any(t => t.m_entity.CompareTag("Player")) && Faction.AreHostile(this.belongsToFactions, RckPlayer.instance.belongsToFactions)
|
|
&& RckPlayer.instance.isAlive))
|
|
{
|
|
if (enemyTargets.Any(t => t.m_entity == Entity.GetPlayerEntity()))
|
|
return;
|
|
|
|
enemyTargets.Add(new VisibleEnemy(Entity.GetPlayerEntity(), RckPlayer.instance.playerAttributes, new AggroInfo(50)));
|
|
|
|
EnterInCombatAgainst(Entity.GetPlayerEntity());
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
|
|
public void ResetComboChain()
|
|
{
|
|
curAttackType = 0;
|
|
}
|
|
|
|
public void ResetComboChainCharged()
|
|
{
|
|
curChargedAttackType = 0;
|
|
}
|
|
|
|
public void FullComboChainReset()
|
|
{
|
|
ResetComboChain();
|
|
ResetComboChainCharged();
|
|
CancelInvoke("ResetComboChain");
|
|
CancelInvoke("ResetComboChainCharged");
|
|
}
|
|
|
|
public void AttackAnimationEvent()
|
|
{
|
|
currentWeaponOnHand.StartCasting(false, lastAttackIndex);
|
|
}
|
|
|
|
public void ChargedAttackAnimationEvent()
|
|
{
|
|
currentWeaponOnHand.StartCasting(true, lastChargedAttackIndex);
|
|
}
|
|
|
|
public void EndAttackAnimationEvent()
|
|
{
|
|
currentWeaponOnHand.StopCasting();
|
|
}
|
|
|
|
public void PlayAttackSound()
|
|
{
|
|
currentWeaponOnHand.PlayOneShot(currentWeaponOnHand.weaponItem.weaponAttacks[curAttackType].attackSound);
|
|
}
|
|
|
|
public void PlayChargedAttackSound()
|
|
{
|
|
currentWeaponOnHand.PlayOneShot(currentWeaponOnHand.weaponItem.weaponChargedAttacks[curChargedAttackType].attackSound);
|
|
}
|
|
|
|
public void ResetAttackStateEvent()
|
|
{
|
|
isAttacking = false;
|
|
canAttack = true;
|
|
}
|
|
|
|
public void DrawWeaponAnimationEvent()
|
|
{
|
|
equipment.currentWeaponOnHip.SetActive(false);
|
|
currentWeaponOnHand.gameObject.SetActive(true);
|
|
}
|
|
|
|
public void UndrawWeaponAnimationEvent()
|
|
{
|
|
currentWeaponOnHand.gameObject.SetActive(false);
|
|
equipment.currentWeaponOnHip.SetActive(true);
|
|
}
|
|
|
|
public void MeleeChargedAttack()
|
|
{
|
|
// Perform a charged attack
|
|
isAttacking = true;
|
|
// Check if the fatigue is enough for the current attack
|
|
if (attributes.CurStamina < equipment.currentWeapon.weaponChargedAttacks[curChargedAttackType].StaminaAmount)
|
|
curChargedAttackType = 0;
|
|
|
|
m_Anim.ResetTrigger("Attack");
|
|
m_Anim.ResetTrigger("ChargedAttack");
|
|
|
|
// Set the animation from the AnimatorController, the AnimationsEvents on the 'Swing' animation will do the job
|
|
m_Anim.SetTrigger("ChargedAttack");
|
|
m_Anim.SetInteger("ChargedAttackType", curChargedAttackType);
|
|
|
|
lastChargedAttackIndex = curChargedAttackType;
|
|
//attributes.AttackFatigue(currentWeapon.weaponChargedAttacks[curChargedAttackType].StaminaAmount); remove stamina!!
|
|
|
|
// To reset the combo if the attack is not done fast
|
|
if (curChargedAttackType + 1 < equipment.currentWeapon.chargedAttackTypes)
|
|
curChargedAttackType++;
|
|
else
|
|
curChargedAttackType = 0;
|
|
|
|
CancelInvoke("ResetComboChainCharged");
|
|
|
|
if (curChargedAttackType != 0)
|
|
Invoke("ResetComboChainCharged", equipment.currentWeapon.chargedAttackChainTime);
|
|
|
|
|
|
// audio
|
|
if (equipment.currentWeapon.weaponChargedAttacks[curChargedAttackType].attackSound != null)
|
|
{
|
|
combatAudioSource.clip = equipment.currentWeapon.weaponChargedAttacks[curChargedAttackType].attackSound;
|
|
combatAudioSource.Play();
|
|
}
|
|
|
|
canAttack = false;
|
|
wantsToAttack = false;
|
|
lastAttackWasCharged = true;
|
|
}
|
|
|
|
[AIInvokable]
|
|
public void MeleeAttack()
|
|
{
|
|
isAttacking = true;
|
|
// Check if the fatigue is enough for the current attack
|
|
//if (attributes.CurStamina < equipment.currentWeapon.weaponAttacks[curAttackType].StaminaAmount)
|
|
// curAttackType = 0;
|
|
|
|
m_Anim.ResetTrigger("Attack");
|
|
|
|
// Set the animation from the AnimatorController, the AnimationsEvents on the 'Swing' animation will do the job
|
|
m_Anim.SetTrigger("Attack");
|
|
m_Anim.SetInteger("AttackType", curAttackType);
|
|
|
|
lastAttackIndex = curAttackType;
|
|
attributes.DamageStamina(equipment.currentWeapon.weaponAttacks[lastAttackIndex].StaminaAmount, true, RCKSettings.DRAIN_STAMINA_ON_ATTACK_SPEEDAMOUNT);
|
|
StopRecoveringStamina();
|
|
InvokeResetRecover();
|
|
|
|
// To reset the combo if the attack is not done fast
|
|
if (curAttackType + 1 < equipment.currentWeapon.AttackTypes)
|
|
curAttackType++;
|
|
else
|
|
curAttackType = 0;
|
|
|
|
CancelInvoke("ResetComboChain");
|
|
|
|
if (curAttackType != 0)
|
|
Invoke("ResetComboChain", equipment.currentWeapon.attackChainTime);
|
|
|
|
canAttack = false;
|
|
wantsToAttack = false;
|
|
lastAttackWasCharged = false;
|
|
}
|
|
|
|
[AIInvokable]
|
|
public void CastSpell()
|
|
{
|
|
if (spellsKnowledge != null)
|
|
{
|
|
if(spellsKnowledge.spellInUse != null)
|
|
{
|
|
if((spellsLimitedByMana && RckPlayer.instance.playerAttributes.CurMana >= spellsKnowledge.spellInUse.manaCost) || !spellsLimitedByMana)
|
|
{
|
|
isAttacking = true;
|
|
isCastingSpell = true;
|
|
|
|
int castHand = (equipment.isUsingShield || (equipment.currentWeapon != null &&
|
|
equipment.currentWeapon.weaponType == WeaponType.Bow)) ? 1 : 0;
|
|
|
|
m_Anim.ResetTrigger("Attack");
|
|
m_Anim.ResetTrigger("CastSpell");
|
|
|
|
m_Anim.SetTrigger("CastSpell");
|
|
m_Anim.SetInteger("CastType", (int)spellsKnowledge.spellInUse.mode);
|
|
m_Anim.SetInteger("CastHand", castHand);
|
|
|
|
attributes.DamageMana(spellsKnowledge.spellInUse.manaCost, true, RCKSettings.DRAIN_MANA_ON_CAST_SPEEDAMOUNT);
|
|
|
|
canAttack = false;
|
|
wantsToAttack = false;
|
|
lastAttackWasCharged = false;
|
|
}
|
|
// else not enough mana
|
|
} //else no spell equipped
|
|
}
|
|
else
|
|
Debug.LogWarning("AI " + entityID + " tried to cast spell, but no Spell Knowledge was set. You may have to assign Spells Knowledge in the Combat Tab.");
|
|
}
|
|
|
|
public void RangedAttack()
|
|
{
|
|
// Check if there is ammo (and if it's of correct type
|
|
if (equipment.itemsEquipped[(int)EquipmentSlots.Ammo] != null && equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item != null && equipment.itemsEquipped[(int)EquipmentSlots.Ammo].Amount >= 1)
|
|
{
|
|
// Player wants to nock an arrow
|
|
wantsToAttack = true;
|
|
|
|
isAttacking = true;
|
|
|
|
curAttackType = 0; // you may choose different shot types (ex. for the crouch)
|
|
|
|
m_Anim.ResetTrigger("Attack");
|
|
|
|
// Set the animation from the AnimatorController, the AnimationsEvents on the 'Swing' animation will do the job
|
|
m_Anim.SetTrigger("Attack");
|
|
m_Anim.SetInteger("AttackType", curAttackType);
|
|
|
|
lastAttackIndex = curAttackType;
|
|
|
|
//cameraAnim.AttackAnimation(currentWeapon.weaponAttacks[curAttackType].cameraAnim);
|
|
|
|
lastAttackIndex = curAttackType;
|
|
//attributes.AttackFatigue(currentWeapon.weaponAttacks[curAttackType].StaminaAmount);
|
|
|
|
// audio
|
|
if (equipment.currentWeapon.weaponAttacks[curAttackType].attackSound != null)
|
|
{
|
|
combatAudioSource.clip = equipment.currentWeapon.weaponAttacks[curAttackType].attackSound;
|
|
combatAudioSource.Play();
|
|
}
|
|
|
|
canAttack = false;
|
|
lastAttackWasCharged = false;
|
|
}
|
|
}
|
|
|
|
[AIInvokable]
|
|
public void SetBlocking(bool startBlocking)
|
|
{
|
|
hasToBlock = startBlocking;
|
|
}
|
|
|
|
public void BlockToggle()
|
|
{
|
|
hasToBlock = !hasToBlock;
|
|
}
|
|
|
|
public bool wasBlocking = false;
|
|
public void BlockCheck()
|
|
{
|
|
isBlocking = hasToBlock;
|
|
|
|
m_Anim.SetBool("isBlocking", isBlocking);
|
|
//fpcAnim.SetBool("isUnbalanced", isUnbalanced);
|
|
|
|
if (isBlocking && !wasBlocking)
|
|
{
|
|
m_Anim.ResetTrigger("hasBlockedAttack");
|
|
m_Anim.SetTrigger("StartBlocking");
|
|
}
|
|
|
|
if (isBlocking)
|
|
{
|
|
if (!wasBlocking)
|
|
{
|
|
//attributes.BlockingFatigue(); TODO
|
|
wasBlocking = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (blockingArea.activeSelf)
|
|
blockingArea.SetActive(false);
|
|
|
|
if (wasBlocking)
|
|
{
|
|
//playerVitals.StopBlockingFatigue();
|
|
wasBlocking = false;
|
|
isBlocking = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by an Animation event on the block idle that enables the blocking area
|
|
/// </summary>
|
|
public void BlockAnimationEvent()
|
|
{
|
|
blockingArea.SetActive(true);
|
|
}
|
|
|
|
public override void DamageBlocked(DamageContext damageContext)
|
|
{
|
|
base.DamageBlocked(damageContext);
|
|
|
|
// If the attack was blocked reduce the damage
|
|
float blockingMultiplier = (equipment.isUsingShield) ? ((ArmorItem)equipment.itemsEquipped[(int)EquipmentSlots.LHand].item).blockingMultiplier : equipment.currentWeapon.BlockingMultiplier;
|
|
|
|
AudioClip blockingSound = (equipment.isUsingShield) ? null : equipment.currentWeapon.blockSound;
|
|
|
|
if (blockingSound != null)
|
|
currentWeaponOnHand.PlayOneShot(blockingSound);
|
|
|
|
damageContext.amount *= blockingMultiplier;
|
|
|
|
attributes.DamageStamina(damageContext.amount * blockingMultiplier, true, RCKSettings.DRAIN_STAMINA_ON_ATTACKBLOCKED_SPEEDAMOUNT);
|
|
|
|
if (attributes.CurStamina <= 0)
|
|
{
|
|
attributes.CurStamina = 0;
|
|
isUnbalanced = true;
|
|
isBlocking = false;
|
|
m_Anim.SetTrigger("hasBeenUnbalanced");
|
|
}
|
|
else
|
|
{
|
|
m_Anim.SetTrigger("hasBlockedAttack");
|
|
}
|
|
|
|
Damage(damageContext);
|
|
}
|
|
|
|
public override void Die()
|
|
{
|
|
hasToBlock = false;
|
|
|
|
// If the AI had a shield equipped
|
|
if(equipment.isUsingShield)
|
|
{
|
|
// Spawn the item in the world
|
|
ItemInWorld itemInWorld = Instantiate(equipment.currentShield.itemInWorld, bodyData.lHand.transform.position, bodyData.lHand.transform.rotation).GetComponent<ItemInWorld>();
|
|
itemInWorld.isCreatedItem = true;
|
|
|
|
var allItems = SaveSystem.SaveSystemManager.instance.saveFile.CreatedItemsInWorldData.allCreatedItemsInWorld;
|
|
// Add this created item
|
|
if (allItems.ContainsKey(WorldManager.instance.currentCenterCell.ID))
|
|
allItems[WorldManager.instance.currentCenterCell.ID].itemsInThis.Add(itemInWorld.ToCreatedItemSaveData());
|
|
else
|
|
{
|
|
allItems.Add(WorldManager.instance.currentCenterCell.ID, new SaveSystem.CreatedItemInWorldCollection());
|
|
allItems[WorldManager.instance.currentCenterCell.ID].itemsInThis.Add(itemInWorld.ToCreatedItemSaveData());
|
|
}
|
|
|
|
equipment.currentShieldObject.SetActive(false);
|
|
equipment.Unequip(EquipmentSlots.LHand);
|
|
inventory.RemoveItem(equipment.currentShield.ItemID, 1);
|
|
itemInWorld.Metadata = inventory.GetItem(equipment.currentShield.ItemID).metadata;
|
|
}
|
|
|
|
// If the AI weapon was drawn
|
|
if (isInCombat && currentWeaponOnHand != null)
|
|
{
|
|
// Stop casting (entity may have died while attacking)
|
|
currentWeaponOnHand.StopCasting();
|
|
|
|
// Instantiate the InWorld equivalent
|
|
if(equipment.currentWeapon.itemInWorld != null)
|
|
{
|
|
// Spawn the item in the world
|
|
ItemInWorld itemInWorld = Instantiate(equipment.currentWeapon.itemInWorld, bodyData.lHand.transform.position, bodyData.lHand.transform.rotation).GetComponent<ItemInWorld>();
|
|
itemInWorld.isCreatedItem = true;
|
|
|
|
var allItems = SaveSystem.SaveSystemManager.instance.saveFile.CreatedItemsInWorldData.allCreatedItemsInWorld;
|
|
// Add this created item
|
|
if (allItems.ContainsKey(WorldManager.instance.currentCenterCell.ID))
|
|
allItems[WorldManager.instance.currentCenterCell.ID].itemsInThis.Add(itemInWorld.ToCreatedItemSaveData());
|
|
else
|
|
{
|
|
allItems.Add(WorldManager.instance.currentCenterCell.ID, new SaveSystem.CreatedItemInWorldCollection());
|
|
allItems[WorldManager.instance.currentCenterCell.ID].itemsInThis.Add(itemInWorld.ToCreatedItemSaveData());
|
|
}
|
|
itemInWorld.Metadata = inventory.GetItem(equipment.currentWeapon.ItemID).metadata;
|
|
}
|
|
|
|
currentWeaponOnHand.gameObject.SetActive(false);
|
|
inventory.RemoveItem(equipment.currentWeapon.ItemID, 1);
|
|
}
|
|
|
|
StopAllCoroutines();
|
|
|
|
m_isInCombat = false;
|
|
isInCombat = false;
|
|
|
|
if (currentSpellProjectile != null && !currentSpellProjectile.shot)
|
|
DestroyImmediate(currentSpellProjectile.gameObject);
|
|
|
|
base.Die();
|
|
|
|
blockingArea.SetActive(false);
|
|
}
|
|
|
|
public bool isSwitchingWeapon = false;
|
|
|
|
public void ChangeWeapon(WeaponItem _weaponToEquip)
|
|
{
|
|
isSwitchingWeapon = true;
|
|
|
|
ItemInInventory itemToEquip;
|
|
// Check if item exists in inventory
|
|
if ((itemToEquip = inventory.GetItem(_weaponToEquip)) != null)
|
|
StartCoroutine(ChangeWeaponTask(itemToEquip));
|
|
}
|
|
|
|
public void ChangeWeapon(string weaponID)
|
|
{
|
|
isSwitchingWeapon = true;
|
|
|
|
ItemInInventory itemToEquip;
|
|
// Check if item exists in inventory
|
|
if( (itemToEquip = inventory.GetItem(weaponID)) != null)
|
|
{
|
|
StartCoroutine(ChangeWeaponTask(itemToEquip));
|
|
}
|
|
}
|
|
|
|
|
|
public IEnumerator ChangeWeaponTask(ItemInInventory _itemToEquip)
|
|
{
|
|
hasToBlock = false;
|
|
|
|
bool wasDrawn = weaponDrawn;
|
|
bool wasInCombat = m_isInCombat;
|
|
if (isInCombat)
|
|
{
|
|
while (!canAttack || isDrawingWeapon)
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
// Undraw the weapon
|
|
UndrawWeapon();
|
|
}
|
|
|
|
while (isInCombat || isDrawingWeapon)
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
equipment.Equip(_itemToEquip.item);
|
|
|
|
OnEquipmentChangesHands();
|
|
OnEquipmentChangesAmmo();
|
|
|
|
if (wasInCombat)
|
|
DrawWeapon();
|
|
else
|
|
isSwitchingWeapon = false;
|
|
|
|
yield return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the caller has ammo for the equipped weapon and equips it, otherwise it returns false without equipping anything.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool EquipAmmoForCurrentWeapon()
|
|
{
|
|
AmmoItem curAmmo = null;
|
|
|
|
switch(currentWeaponOnHand.weaponItem.weaponType)
|
|
{
|
|
case WeaponType.Bow:
|
|
for(int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type == AmmoItem.AmmoType.Arrow)
|
|
{
|
|
equipment.Equip(curAmmo);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WeaponType.Crossbow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type == AmmoItem.AmmoType.Dart)
|
|
{
|
|
equipment.Equip(curAmmo);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the caller has ammo for the equipped weapon and equips it, otherwise it returns false without equipping anything.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool EquipAmmoForWeapon(WeaponItem _weapon)
|
|
{
|
|
AmmoItem curAmmo = null;
|
|
|
|
switch (_weapon.weaponType)
|
|
{
|
|
case WeaponType.Bow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type.HasFlag(AmmoItem.AmmoType.Arrow))
|
|
{
|
|
equipment.Equip(curAmmo);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WeaponType.Crossbow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type.HasFlag(AmmoItem.AmmoType.Dart))
|
|
{
|
|
equipment.Equip(curAmmo);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the AmmoItem if the caller has ammo for the equipped weapon, otherwise it returns null.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public AmmoItem HasAmmoForCurrentWeapon()
|
|
{
|
|
AmmoItem curAmmo = null;
|
|
|
|
switch (currentWeaponOnHand.weaponItem.weaponType)
|
|
{
|
|
case WeaponType.Bow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type == AmmoItem.AmmoType.Arrow)
|
|
{
|
|
return curAmmo;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WeaponType.Crossbow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type == AmmoItem.AmmoType.Dart)
|
|
{
|
|
return(curAmmo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the AmmoItem if the caller has ammo for the weapon passed, otherwise it returns null.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public AmmoItem HasAmmoForWeapon(WeaponItem _weapon)
|
|
{
|
|
AmmoItem curAmmo = null;
|
|
|
|
switch (_weapon.weaponType)
|
|
{
|
|
case WeaponType.Bow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type.HasFlag(AmmoItem.AmmoType.Arrow))
|
|
{
|
|
return curAmmo;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WeaponType.Crossbow:
|
|
for (int i = 0; i < inventory.subLists[(int)ItemTypes.AmmoItem].Count; i++)
|
|
{
|
|
curAmmo = inventory.subLists[(int)ItemTypes.AmmoItem][i].item as AmmoItem;
|
|
if (curAmmo.type.HasFlag(AmmoItem.AmmoType.Dart))
|
|
{
|
|
return (curAmmo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// TODO
|
|
public bool SwitchToBestMeleeWeapon()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public bool SwitchToBestRangedWeapon()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public bool SwitchToBestAmmo()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public bool EquipShieldIfHeCan()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
GameObject projectile;
|
|
public void SpawnAmmoOnRHand()
|
|
{
|
|
projectile = Instantiate(((AmmoItem)equipment.currentAmmo).projectile, bodyData.rHand);
|
|
currentProjectile = projectile.GetComponent<Projectile>();
|
|
currentProjectile.shooterEntity = this;
|
|
}
|
|
|
|
public bool projectileNocked = false;
|
|
public void OnAmmoIsReady()
|
|
{
|
|
projectileNocked = true;
|
|
}
|
|
|
|
[AIInvokable]
|
|
public void RangedLoadAndAim()
|
|
{
|
|
StopCoroutine(RangedLoadAndAimTask());
|
|
StartCoroutine(RangedLoadAndAimTask());
|
|
}
|
|
|
|
private IEnumerator RangedLoadAndAimTask()
|
|
{
|
|
// Player wants to nock an arrow
|
|
wantsToAttack = true;
|
|
|
|
isAttacking = true;
|
|
|
|
curAttackType = 0; // you may choose different shot types (ex. for the crouch)
|
|
|
|
m_Anim.ResetTrigger("Attack");
|
|
|
|
// Set the animation from the AnimatorController
|
|
m_Anim.SetTrigger("Attack");
|
|
m_Anim.SetInteger("AttackType", curAttackType);
|
|
|
|
equipment.currentWeaponAnimator.ResetTrigger("Load");
|
|
equipment.currentWeaponAnimator.Play("Load");
|
|
|
|
//currentWeaponAnimator.SetTrigger("Load");
|
|
|
|
//fpcAnim.Play("Load");
|
|
|
|
//cameraAnim.AttackAnimation(currentWeapon.weaponAttacks[curAttackType].cameraAnim);
|
|
|
|
lastAttackIndex = curAttackType;
|
|
//playerVitals.AttackFatigue(currentWeapon.weaponAttacks[curAttackType].StaminaAmount);
|
|
|
|
// audio
|
|
//if (currentWeapon.weaponAttacks[curAttackType].attackSound != null)
|
|
//{
|
|
// combatAudioSource.clip = currentWeapon.weaponAttacks[curAttackType].attackSound;
|
|
// combatAudioSource.Play();
|
|
//}
|
|
|
|
canAttack = false;
|
|
lastAttackWasCharged = false;
|
|
|
|
//aiLookAt.ForceToLookAtTarget(mainTarget);
|
|
|
|
Vector3 _shootDir = (mainTarget.transform.position - this.transform.position).normalized;
|
|
|
|
Vector3 noiseDir = Vector3.zero;
|
|
bool abort = false;
|
|
while (!projectileNocked && !abort)
|
|
{
|
|
// Calculate some noise
|
|
noiseDir = new Vector3(Random.Range(-1f, 1f) / attributes.derivedAttributes.rangedCombatAccuracy * 100,
|
|
Random.Range(-1f, 1f) / attributes.derivedAttributes.rangedCombatAccuracy * 100,
|
|
0);
|
|
// Aim preditct etc
|
|
|
|
if (mainTarget == null || !m_isInCombat)
|
|
{
|
|
abort = true;
|
|
break;
|
|
}
|
|
|
|
if (mainTarget.CompareTag("Player"))
|
|
{
|
|
_shootDir = ((mainTarget.transform.position - this.transform.position) + noiseDir + (Vector3.down * (Player.RckPlayer.instance.IsCrouching ? 2.25f : 1.5f))).normalized;
|
|
}
|
|
else
|
|
_shootDir = ((mainTarget.transform.position - this.transform.position) + noiseDir).normalized;
|
|
|
|
yield return new WaitForSeconds(0.1f);
|
|
}
|
|
|
|
if(abort)
|
|
{
|
|
m_Anim.SetTrigger("AbortShoot");
|
|
if(currentProjectile != null)
|
|
Destroy(currentProjectile.gameObject);
|
|
projectileNocked = false;
|
|
wantsToAttack = false;
|
|
isAttacking = false;
|
|
canAttack = true;
|
|
}
|
|
|
|
// Aim
|
|
RangedShoot(_shootDir);
|
|
}
|
|
|
|
public void RangedShoot(Vector3 _direction)
|
|
{
|
|
m_Anim.ResetTrigger("Attack");
|
|
|
|
m_Anim.SetTrigger("Attack");
|
|
m_Anim.SetInteger("AttackType", 1);
|
|
|
|
equipment.currentWeaponAnimator.ResetTrigger("Shoot");
|
|
equipment.currentWeaponAnimator.SetTrigger("Shoot");
|
|
|
|
projectileNocked = false;
|
|
wantsToAttack = false;
|
|
isAttacking = false;
|
|
|
|
// Shoot the arrow
|
|
currentProjectile.Shoot(equipment.currentWeapon, (AmmoItem)equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item, this, _direction, false);
|
|
//inventory.RemoveItem()
|
|
|
|
aiLookAt.Invoke("StopForcingLookAtTarget", 1f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Those callbacks will be called by the functionalities of the BehaviourTree.
|
|
/// The actual implementations and behaviours of those must be defined per each entity.
|
|
/// </summary>
|
|
|
|
void IMeleeAttacker.BlockEvent()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.BlockEvent()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.DrawWeapon()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.DrawWeapon()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.MeleeAttackCheck()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.MeleeAttackEvent()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.OnEnterCombat()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.OnEnterCombat()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.RangedAttackCheck()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.RangedAttackEvent()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.UndrawWeapon()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.UndrawWeapon()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IMeleeAttacker.UseMelee()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
void IRangedAttacker.UseRanged()
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
public void OnAttackIsBlocked()
|
|
{
|
|
canAttack = false;
|
|
wantsToAttack = false;
|
|
isAttacking = true;
|
|
isBlocking = false;
|
|
FullComboChainReset();
|
|
|
|
m_Anim.SetTrigger("hasBeenBlocked");
|
|
}
|
|
|
|
[AIInvokable]
|
|
public void Get2DTargetDistance()
|
|
{
|
|
if (mainTarget != null)
|
|
targetDistance2D = Calculate2DTargetDistance(transform.position, mainTarget.transform.position);
|
|
else
|
|
targetDistance2D = 999999.9f;
|
|
}
|
|
|
|
private float Calculate2DTargetDistance(Vector3 v1, Vector3 v2)
|
|
{
|
|
float xDiff = v1.x - v2.x;
|
|
float zDiff = v1.z - v2.z;
|
|
return Mathf.Sqrt((xDiff * xDiff) + (zDiff * zDiff));
|
|
}
|
|
|
|
// Spells, Update 1.3 - Anim callbacks
|
|
|
|
public void CastSpellInit()
|
|
{
|
|
Spell curSpell = spellsKnowledge.spellInUse;
|
|
|
|
Transform handT = (equipment.isUsingShield || (equipment.currentWeapon != null && equipment.currentWeapon.weaponType == WeaponType.Bow)) ? bodyData.rHand : bodyData.lHand;
|
|
|
|
switch (curSpell.mode)
|
|
{
|
|
case SpellModes.Projectile:
|
|
spellProjectile = Instantiate(curSpell.magicProjectile, handT);
|
|
spellProjectile.SetLayer(RCKLayers.Default, true);
|
|
currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
|
|
break;
|
|
|
|
case SpellModes.Touch:
|
|
spellProjectile = Instantiate(curSpell.magicProjectile, handT);
|
|
spellProjectile.SetLayer(RCKLayers.Default, true);
|
|
currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
|
|
currentSpellProjectile.Init(this, false);
|
|
break;
|
|
|
|
case SpellModes.Self:
|
|
spellProjectile = Instantiate(curSpell.magicProjectile, handT);
|
|
spellProjectile.SetLayer(RCKLayers.Default, true);
|
|
currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
|
|
currentSpellProjectile.Init(this, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void CastSpellAttackEvent()
|
|
{
|
|
// Instantiate/Do Stuff
|
|
Spell curSpell = spellsKnowledge.spellInUse;
|
|
Vector3 _shootDir;
|
|
switch (curSpell.mode)
|
|
{
|
|
case SpellModes.Projectile:
|
|
_shootDir = ((mainTarget.transform.position - this.transform.position) + (Vector3.down * (Player.RckPlayer.instance.IsCrouching ? 2.25f : 1.5f))).normalized;
|
|
currentSpellProjectile.Shoot(this, _shootDir, false);
|
|
currentSpellProjectile.ExecuteOnCasterEffects();
|
|
break;
|
|
|
|
case SpellModes.Touch:
|
|
_shootDir = ((mainTarget.transform.position - this.transform.position) + (Vector3.down * (Player.RckPlayer.instance.IsCrouching ? 2.25f : 1.5f))).normalized;
|
|
if (currentSpellProjectile != null)
|
|
{
|
|
currentSpellProjectile.Shoot(this, _shootDir, false);
|
|
currentSpellProjectile.ExecuteOnCasterEffects();
|
|
}
|
|
break;
|
|
|
|
case SpellModes.Self:
|
|
currentSpellProjectile.Explode();
|
|
currentSpellProjectile.ExecuteOnCasterEffects();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void CastSpellResetEvent()
|
|
{
|
|
ResetAttackStateEvent();
|
|
isAttacking = false;
|
|
isCastingSpell = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ignores Faction and just engages with the player if player is visible
|
|
/// </summary>
|
|
public void TryEngageWithPlayer()
|
|
{
|
|
if (!isLoaded || isInConversation) // if this is not loaded skip until it is
|
|
return;
|
|
|
|
try
|
|
{
|
|
if (!m_isInCombat && visibleTargets.Any(t => t.tr.CompareTag("Player")))
|
|
{
|
|
if (enemyTargets.Any(t => t.m_entity == Entity.GetPlayerEntity()))
|
|
return;
|
|
|
|
enemyTargets.Add(new VisibleEnemy(Entity.GetPlayerEntity(), RckPlayer.instance.playerAttributes, new AggroInfo(50)));
|
|
|
|
EnterInCombatAgainst(Entity.GetPlayerEntity());
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
} |