2023-03-28 13:16:30 -04:00
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
using RPGCreationKit ;
using RPGCreationKit.BehaviourTree ;
using System.Linq ;
using RPGCreationKit.Player ;
namespace RPGCreationKit.AI
{
2023-05-11 01:27:44 -04:00
/// <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
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
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 ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
public GameObject blockingArea ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
public int YNewProperty
{ get ; set ; }
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
public float CurrentHealth
{
get
{
return attributes . CurHealth ;
}
}
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
public Projectile currentProjectile ;
public AudioSource combatAudioSource ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
// Spells Related
GameObject spellProjectile ;
SpellProjectile currentSpellProjectile ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
[Space(10)]
[Header("Status")]
public float targetDistance2D = 99999.9f ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
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 ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
public bool lastAttackWasCharged ;
public int curAttackType = 0 ;
public int curChargedAttackType = 0 ;
[HideInInspector] public int lastAttackIndex = 0 ;
[HideInInspector] public int lastChargedAttackIndex = 0 ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
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 ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
combatType = ( int ) equipment . currentWeapon . weaponType ;
m_Anim . SetInteger ( "CombatType" , combatType ) ;
weaponDrawn = false ;
m_Anim . SetBool ( "isDrawn" , false ) ;
m_Anim . SetTrigger ( "Undraw" ) ;
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
wantsToAttack = false ;
}
2023-03-28 13:16:30 -04:00
2023-05-11 01:27:44 -04:00
/// <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 ;
2023-06-21 12:09:52 -04:00
if ( spellProjectile ! = null )
Destroy ( spellProjectile ) ;
2023-05-11 01:27:44 -04:00
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 :
2023-06-21 12:09:52 -04:00
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 ;
2023-05-11 01:27:44 -04:00
currentSpellProjectile . Shoot ( this , _shootDir , false ) ;
currentSpellProjectile . ExecuteOnCasterEffects ( ) ;
break ;
case SpellModes . Touch :
2023-06-21 12:09:52 -04:00
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 ;
2023-05-11 01:27:44 -04:00
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 { }
}
}
2023-03-28 13:16:30 -04:00
}