Firstborn/Assets/RPG Creation Kit/Scripts/AI/AI Scripts/AICombatSystem.cs
Schaken-Mods f35c8a6d6e 6/21/23 update
Upgraded the framework which includes the Distant cell rendering. I have been merging meshes to cut down the Draw calls, so far gaining on average 20FPS everywhere. New bugs are the magic fails (Again) and the environment presets fail to call when outside. Currently trying to build new lightmaps for the combined meshes.
2023-06-21 11:09:52 -05:00

1342 lines
36 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 (spellProjectile != null)
Destroy(spellProjectile);
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:
if(mainTarget != null)
_shootDir = ((mainTarget.transform.position - this.transform.position) + (Vector3.down * (Player.RckPlayer.instance.IsCrouching ? 2.25f : 1.5f))).normalized;
else
_shootDir = (((transform.forward * 2) - 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:
if(mainTarget != null)
_shootDir = ((mainTarget.transform.position - this.transform.position) + (Vector3.down * (Player.RckPlayer.instance.IsCrouching ? 2.25f : 1.5f))).normalized;
else
_shootDir = (((transform.forward*2) - 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")))// use factions also. get owners faction/ then check peoples factions. also alert crime faction.
{
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 { }
}
}
}