2018-03-29 17:01:57 -04:00
2019-07-07 20:56:15 +00:00
# include <StdAfx.h>
2018-03-29 17:01:57 -04:00
# include "MonsterAI.h"
# include "WeenieObject.h"
# include "Monster.h"
# include "World.h"
# include "SpellcastingManager.h"
# include "EmoteManager.h"
2018-07-20 18:19:00 -05:00
# include "CombatSystem.h"
2018-03-29 17:01:57 -04:00
2018-06-25 16:13:15 +01:00
bool monster_brawl = 0 ;
2018-03-29 17:01:57 -04:00
# define DEFAULT_AWARENESS_RANGE 40.0
2018-07-12 14:41:21 -07:00
MonsterAIManager : : MonsterAIManager ( CMonsterWeenie * pWeenie , const Position & HomePos )
2018-03-29 17:01:57 -04:00
{
m_pWeenie = pWeenie ;
_toleranceType = pWeenie - > InqIntQuality ( TOLERANCE_INT , 0 , TRUE ) ;
_aiOptions = pWeenie - > InqIntQuality ( AI_OPTIONS_INT , 0 , TRUE ) ;
2018-07-12 14:41:21 -07:00
_cachedVisualAwarenessRange = m_pWeenie - > InqFloatQuality ( VISUAL_AWARENESS_RANGE_FLOAT , DEFAULT_AWARENESS_RANGE ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
if ( ! m_pWeenie - > GetWieldedCombat ( COMBAT_USE_TWO_HANDED ) )
_meleeWeapon = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_MELEE ) ;
2018-04-02 16:34:35 -05:00
else {
2018-07-12 14:41:21 -07:00
_meleeWeapon = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_TWO_HANDED ) ;
2018-04-02 16:34:35 -05:00
}
2018-07-12 14:41:21 -07:00
_missileWeapon = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_MISSILE ) ;
_shield = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_SHIELD ) ;
2018-03-29 17:01:57 -04:00
SKILL_ADVANCEMENT_CLASS unarmedSkill ;
2018-07-12 14:41:21 -07:00
m_pWeenie - > m_Qualities . InqSkillAdvancementClass ( LIGHT_WEAPONS_SKILL , unarmedSkill ) ;
2018-03-29 17:01:57 -04:00
_hasUnarmedSkill = ( unarmedSkill > UNTRAINED_SKILL_ADVANCEMENT_CLASS ) ;
2018-07-12 14:41:21 -07:00
if ( _meleeWeapon ! = NULL & & _missileWeapon ! = NULL ) //if we have both melee and missile weapons, favor missile
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
m_pWeenie - > FinishMoveItemToContainer ( _meleeWeapon , m_pWeenie , 0 , true ) ;
if ( _shield & & _missileWeapon - > InqIntQuality ( DEFAULT_COMBAT_STYLE_INT , 0 ) ! = ThrownWeapon_CombatStyle )
m_pWeenie - > FinishMoveItemToContainer ( _shield , m_pWeenie , 0 , true ) ;
2018-03-29 17:01:57 -04:00
else
_currentShield = _shield ;
_currentWeapon = _missileWeapon ;
}
2018-07-12 14:41:21 -07:00
else if ( _missileWeapon ! = NULL )
2018-03-29 17:01:57 -04:00
{
_currentWeapon = _missileWeapon ;
2018-07-12 14:41:21 -07:00
if ( _shield & & _missileWeapon - > InqIntQuality ( DEFAULT_COMBAT_STYLE_INT , 0 ) ! = ThrownWeapon_CombatStyle )
m_pWeenie - > FinishMoveItemToContainer ( _shield , m_pWeenie , 0 , true ) ;
2018-03-29 17:01:57 -04:00
else
_currentShield = _shield ;
}
2018-07-12 14:41:21 -07:00
else if ( _meleeWeapon ! = NULL )
2018-03-29 17:01:57 -04:00
{
_currentWeapon = _meleeWeapon ;
_currentShield = _shield ;
}
if ( pWeenie - > m_Qualities . _emote_table & & pWeenie - > m_Qualities . _emote_table - > _emote_table . lookup ( Taunt_EmoteCategory ) )
_nextTaunt = Timer : : cur_time + Random : : GenUInt ( 10 , 30 ) ; //We have taunts so schedule them.
2018-09-22 21:48:46 -07:00
pWeenie - > transient_state | = CONTACT_TS ;
2018-03-29 17:01:57 -04:00
// List of qualities that could be used to affect the AI behavior:
// AI_CP_THRESHOLD_INT
// AI_PP_THRESHOLD_INT
// AI_ADVANCEMENT_STRATEGY_INT
// AI_ALLOWED_COMBAT_STYLE_INT
// COMBAT_TACTIC_INT
// TARGETING_TACTIC_INT
// HOMESICK_TARGETING_TACTIC_INT
// AI_OPTIONS_INT
// FRIEND_TYPE_INT
// FOE_TYPE_INT
// ATTACKER_AI_BOOL
// AI_USES_MANA_BOOL
// AI_USE_HUMAN_MAGIC_ANIMATIONS_BOOL
// AI_IMMOBILE_BOOL
// AI_ALLOW_TRADE_BOOL
// AI_ACCEPT_EVERYTHING_BOOL
// AI_USE_MAGIC_DELAY_FLOAT
// AI_ACQUIRE_HEALTH_FLOAT
// AI_ACQUIRE_STAMINA_FLOAT
// AI_ACQUIRE_MANA_FLOAT
// AI_COUNTERACT_ENCHANTMENT_FLOAT
// AI_DISPEL_ENCHANTMENT_FLOAT
// AI_TARGETED_DETECTION_RADIUS_FLOAT
// SetHomePosition(HomePos); don't use preset home position
}
MonsterAIManager : : ~ MonsterAIManager ( )
{
}
void MonsterAIManager : : SetHomePosition ( const Position & pos )
{
m_HomePosition = pos ;
}
void MonsterAIManager : : Update ( )
{
if ( ! m_HomePosition . objcell_id )
{
// make sure we set a home position
2018-07-12 14:41:21 -07:00
if ( ! ( m_pWeenie - > transient_state & ON_WALKABLE_TS ) )
2018-03-29 17:01:57 -04:00
return ;
2018-07-12 14:41:21 -07:00
SetHomePosition ( m_pWeenie - > m_Position ) ;
2018-03-29 17:01:57 -04:00
}
switch ( m_State )
{
case MonsterAIState : : Idle :
UpdateIdle ( ) ;
break ;
case MonsterAIState : : MeleeModeAttack :
UpdateMeleeModeAttack ( ) ;
break ;
case MonsterAIState : : MissileModeAttack :
UpdateMissileModeAttack ( ) ;
break ;
case MonsterAIState : : ReturningToSpawn :
UpdateReturningToSpawn ( ) ;
break ;
case MonsterAIState : : SeekNewTarget :
UpdateSeekNewTarget ( ) ;
break ;
}
}
void MonsterAIManager : : SwitchState ( int state )
{
if ( state = = m_State )
return ;
switch ( state )
{
case MonsterAIState : : Idle :
EndIdle ( ) ;
break ;
case MonsterAIState : : MeleeModeAttack :
EndMeleeModeAttack ( ) ;
break ;
case MonsterAIState : : MissileModeAttack :
EndMissileModeAttack ( ) ;
break ;
case MonsterAIState : : ReturningToSpawn :
EndReturningToSpawn ( ) ;
break ;
case MonsterAIState : : SeekNewTarget :
EndSeekNewTarget ( ) ;
break ;
}
EnterState ( state ) ;
}
void MonsterAIManager : : EnterState ( int state )
{
m_State = state ;
switch ( state )
{
case MonsterAIState : : Idle :
BeginIdle ( ) ;
break ;
case MonsterAIState : : MeleeModeAttack :
BeginMeleeModeAttack ( ) ;
break ;
case MonsterAIState : : MissileModeAttack :
BeginMissileModeAttack ( ) ;
break ;
case MonsterAIState : : ReturningToSpawn :
BeginReturningToSpawn ( ) ;
break ;
case MonsterAIState : : SeekNewTarget :
BeginSeekNewTarget ( ) ;
break ;
}
}
void MonsterAIManager : : BeginIdle ( )
{
2018-05-29 23:47:16 +01:00
2018-03-29 17:01:57 -04:00
m_fNextPVSCheck = Timer : : cur_time ;
2018-07-11 04:53:32 +00:00
m_pWeenie - > ChangeCombatMode ( COMBAT_MODE : : NONCOMBAT_COMBAT_MODE , false ) ;
2018-06-25 16:13:15 +01:00
if ( monster_brawl )
2018-06-26 10:11:44 +01:00
{
2018-07-11 04:53:32 +00:00
m_pWeenie - > m_MonsterAI - > _toleranceType = TolerateNothing ;
2018-06-26 10:11:44 +01:00
}
2018-07-11 04:53:32 +00:00
2018-03-29 17:01:57 -04:00
}
void MonsterAIManager : : EndIdle ( )
{
}
void MonsterAIManager : : UpdateIdle ( )
{
2018-06-25 16:13:15 +01:00
if ( _toleranceType = = TolerateNothing | | monster_brawl )
2018-03-29 17:01:57 -04:00
{
SeekTarget ( ) ;
}
}
bool MonsterAIManager : : SeekTarget ( )
{
if ( m_fNextPVSCheck < = Timer : : cur_time )
{
m_fNextPVSCheck = Timer : : cur_time + 2.0f ;
2018-07-11 04:53:32 +00:00
std : : list < CWeenieObject * > results ;
2019-05-01 21:14:45 +00:00
g_pWorld - > EnumNearby ( m_pWeenie , _cachedVisualAwarenessRange , & results ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
std : : list < CWeenieObject * > validTargets ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
CWeenieObject * pClosestWeenie = NULL ;
double fClosestWeenieDist = FLT_MAX ;
2018-03-29 17:01:57 -04:00
for ( auto weenie : results )
{
2019-05-01 21:14:45 +00:00
if ( ! IsValidTarget ( weenie ) )
2018-03-29 17:01:57 -04:00
continue ;
2019-05-29 17:04:25 -07:00
if ( ! weenie - > IsAttackable ( ) )
2018-03-29 17:01:57 -04:00
continue ;
validTargets . push_back ( weenie ) ;
}
if ( ! validTargets . empty ( ) )
{
2019-07-17 04:16:10 +00:00
CWeenieObject * target = nullptr ;
2019-05-29 17:04:25 -07:00
switch ( ( TargetingTacticType ) m_pWeenie - > m_Qualities . GetInt ( TARGETING_TACTIC_INT , 0 ) )
{
case Target_Nearest :
{
float dist = 10 ;
for ( auto it : validTargets )
{
float currentDist = m_pWeenie - > DistanceTo ( it ) ;
if ( currentDist < dist )
{
dist = currentDist ;
target = it ;
}
}
break ;
}
case Target_Weakest :
{
int level = 275 ;
for ( auto it : validTargets )
{
float currentLevel = it - > m_Qualities . GetInt ( LEVEL_INT , 0 ) ;
if ( currentLevel < level )
{
level = currentLevel ;
target = it ;
}
}
break ;
}
case Target_Strongest :
{
int level = 1 ;
for ( auto it : validTargets )
{
float currentLevel = it - > m_Qualities . GetInt ( LEVEL_INT , 0 ) ;
if ( currentLevel > level )
{
level = currentLevel ;
target = it ;
}
}
break ;
}
default : // Attack the highest damager
{
int highDamageAmt = 0 ;
2019-07-07 20:56:15 +00:00
uint32_t highDamager = 0 ;
2019-05-29 17:04:25 -07:00
for ( auto ds : m_pWeenie - > m_aDamageSources )
{
if ( ds . second > highDamageAmt )
{
highDamageAmt = ds . second ;
highDamager = ds . first ;
}
}
target = g_pWorld - > FindObject ( highDamager ) ;
break ;
}
}
if ( ! target ) // Random target if no target found by now
{
std : : list < CWeenieObject * > : : iterator i = validTargets . begin ( ) ;
std : : advance ( i , Random : : GenInt ( 0 , ( unsigned int ) ( validTargets . size ( ) - 1 ) ) ) ;
SetNewTarget ( * i ) ;
}
else
SetNewTarget ( target ) ;
2018-03-29 17:01:57 -04:00
return true ;
}
2018-06-26 10:11:44 +01:00
2018-06-25 16:13:15 +01:00
if ( monster_brawl )
{
2018-07-12 14:41:21 -07:00
SetHomePosition ( m_pWeenie - > m_Position ) ; // this is your home now
2018-06-25 16:13:15 +01:00
}
2018-03-29 17:01:57 -04:00
}
return false ;
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : SetNewTarget ( CWeenieObject * pTarget )
2018-03-29 17:01:57 -04:00
{
m_TargetID = pTarget - > GetID ( ) ;
2018-07-12 14:41:21 -07:00
if ( _missileWeapon ! = NULL )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
double fTargetDist = m_pWeenie - > DistanceTo ( pTarget , true ) ;
if ( fTargetDist > 5 | | ( _meleeWeapon = = NULL & & ! _hasUnarmedSkill ) )
2018-03-29 17:01:57 -04:00
SwitchState ( MissileModeAttack ) ;
else
SwitchState ( MeleeModeAttack ) ;
}
else
SwitchState ( MeleeModeAttack ) ;
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChanceExecuteEmoteSet ( pTarget - > GetID ( ) , NewEnemy_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
}
2018-07-12 14:41:21 -07:00
CWeenieObject * MonsterAIManager : : GetTargetWeenie ( )
2018-03-29 17:01:57 -04:00
{
return g_pWorld - > FindObject ( m_TargetID ) ;
}
float MonsterAIManager : : DistanceToHome ( )
{
2018-07-12 14:41:21 -07:00
if ( ! m_pWeenie )
2018-05-29 23:47:16 +01:00
{
return FLT_MAX ;
}
2018-03-29 17:01:57 -04:00
if ( ! m_HomePosition . objcell_id )
return FLT_MAX ;
2018-07-12 14:41:21 -07:00
return m_HomePosition . distance ( m_pWeenie - > m_Position ) ;
2018-03-29 17:01:57 -04:00
}
bool MonsterAIManager : : ShouldSeekNewTarget ( )
{
2018-06-25 16:13:15 +01:00
if ( monster_brawl )
{
return true ; // we always want a new target
}
2018-07-11 04:53:32 +00:00
2018-03-29 17:01:57 -04:00
if ( DistanceToHome ( ) > = m_fMaxHomeRange )
return false ;
return true ;
}
bool MonsterAIManager : : RollDiceCastSpell ( )
{
if ( m_fNextCastTime > Timer : : cur_time )
{
return false ;
}
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > m_Qualities . _spell_book )
2018-03-29 17:01:57 -04:00
{
/* not correct, these must be independent events (look at wisps)
float dice = Random : : RollDice ( 0.0f , 1.0f ) ;
2018-07-12 14:41:21 -07:00
auto spellIterator = m_pWeenie - > m_Qualities . _spell_book - > _spellbook . begin ( ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
while ( spellIterator ! = m_pWeenie - > m_Qualities . _spell_book - > _spellbook . end ( ) )
2018-03-29 17:01:57 -04:00
{
float likelihood = spellIterator - > second . _casting_likelihood ;
if ( dice < = likelihood )
{
return DoCastSpell ( spellIterator - > first ) ;
}
dice - = likelihood ;
spellIterator + + ;
}
*/
2018-07-12 14:41:21 -07:00
auto spellIterator = m_pWeenie - > m_Qualities . _spell_book - > _spellbook . begin ( ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
while ( spellIterator ! = m_pWeenie - > m_Qualities . _spell_book - > _spellbook . end ( ) )
2018-03-29 17:01:57 -04:00
{
float dice = Random : : RollDice ( 0.0f , 1.0f ) ;
float likelihood = spellIterator - > second . _casting_likelihood ;
if ( dice < = likelihood )
{
2018-07-12 14:41:21 -07:00
m_fNextCastTime = Timer : : cur_time + m_pWeenie - > m_Qualities . GetFloat ( AI_USE_MAGIC_DELAY_FLOAT , 0.0 ) ;
2018-03-29 17:01:57 -04:00
return DoCastSpell ( spellIterator - > first ) ;
}
spellIterator + + ;
}
}
return false ;
}
2019-07-07 20:56:15 +00:00
bool MonsterAIManager : : DoCastSpell ( uint32_t spell_id )
2018-07-12 14:41:21 -07:00
{
CWeenieObject * pTarget = GetTargetWeenie ( ) ;
m_pWeenie - > MakeSpellcastingManager ( ) - > CreatureBeginCast ( pTarget ? pTarget - > GetID ( ) : 0 , spell_id ) ;
2018-03-29 17:01:57 -04:00
return true ;
}
bool MonsterAIManager : : DoMeleeAttack ( )
{
2019-07-07 20:56:15 +00:00
uint32_t motion = 0 ;
2018-03-29 17:01:57 -04:00
ATTACK_HEIGHT height = ATTACK_HEIGHT : : UNDEF_ATTACK_HEIGHT ;
float power = 0.0f ;
GenerateRandomAttack ( & motion , & height , & power ) ;
if ( ! motion )
{
return false ;
}
2018-07-12 14:41:21 -07:00
CWeenieObject * pTarget = GetTargetWeenie ( ) ;
2018-03-29 17:01:57 -04:00
if ( ! pTarget )
{
return false ;
}
2018-07-12 14:41:21 -07:00
m_pWeenie - > TryMeleeAttack ( pTarget - > GetID ( ) , height , power , motion ) ;
2018-03-29 17:01:57 -04:00
2018-07-20 18:19:00 -05:00
m_fNextAttackTime = Timer : : cur_time + 2.0f + power ;
2018-03-29 17:01:57 -04:00
m_fNextChaseTime = Timer : : cur_time ; // chase again anytime
m_fMinCombatStateTime = Timer : : cur_time + m_fMinCombatStateDuration ;
return true ;
}
2019-07-07 20:56:15 +00:00
void MonsterAIManager : : GenerateRandomAttack ( uint32_t * motion , ATTACK_HEIGHT * height , float * power , CWeenieObject * weapon )
2018-03-29 17:01:57 -04:00
{
* motion = 0 ;
* height = ATTACK_HEIGHT : : UNDEF_ATTACK_HEIGHT ;
2019-02-04 16:54:20 -06:00
* power = ( float ) Random : : GenFloat ( ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > _combatTable )
2018-03-29 17:01:57 -04:00
{
2019-07-07 20:56:15 +00:00
if ( weapon = = NULL )
{
if ( ! m_pWeenie - > GetWieldedCombat ( COMBAT_USE_TWO_HANDED ) )
weapon = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_MELEE ) ;
else
weapon = m_pWeenie - > GetWieldedCombat ( COMBAT_USE_TWO_HANDED ) ;
2018-04-02 16:34:35 -05:00
}
2018-03-29 17:01:57 -04:00
if ( weapon )
{
AttackType attackType = ( AttackType ) weapon - > InqIntQuality ( ATTACK_TYPE_INT , 0 ) ;
2019-07-07 20:56:15 +00:00
uint32_t style = m_pWeenie - > get_minterp ( ) - > InqStyle ( ) ;
2018-03-29 17:01:57 -04:00
if ( attackType = = ( Thrust_AttackType | Slash_AttackType ) )
{
if ( * power > = 0.75f )
attackType = Slash_AttackType ;
else
attackType = Thrust_AttackType ;
}
2018-07-20 18:19:00 -05:00
CombatManeuver * combatManeuver = NULL ;
2018-03-29 17:01:57 -04:00
2019-07-07 20:56:15 +00:00
for ( uint32_t i = 0 ; i < m_pWeenie - > _combatTable - > _num_combat_maneuvers ; i + + )
2018-03-29 17:01:57 -04:00
{
2018-07-20 18:19:00 -05:00
int m = Random : : GenInt ( 0 , m_pWeenie - > _combatTable - > _num_combat_maneuvers - 1 ) ;
2018-03-29 17:01:57 -04:00
2018-11-28 14:29:16 -06:00
if ( m_pWeenie - > _combatTable - > _cmt [ m ] . style = = style & & m_pWeenie - > _combatTable - > _cmt [ m ] . attack_type & attackType )
2018-03-29 17:01:57 -04:00
{
2018-07-20 18:19:00 -05:00
combatManeuver = & m_pWeenie - > _combatTable - > _cmt [ m ] ;
break ;
2018-03-29 17:01:57 -04:00
}
}
if ( combatManeuver )
{
* motion = combatManeuver - > motion ;
* height = combatManeuver - > attack_height ;
}
}
else
{
2018-07-20 18:19:00 -05:00
CombatManeuver * combatManeuver = NULL ;
2018-03-29 17:01:57 -04:00
2019-07-07 20:56:15 +00:00
for ( uint32_t i = 0 ; i < m_pWeenie - > _combatTable - > _num_combat_maneuvers ; i + + )
2018-03-29 17:01:57 -04:00
{
2018-07-20 18:19:00 -05:00
int m = Random : : GenInt ( 0 , m_pWeenie - > _combatTable - > _num_combat_maneuvers - 1 ) ;
2018-03-29 17:01:57 -04:00
2018-07-20 18:19:00 -05:00
if ( m_pWeenie - > _combatTable - > _cmt [ m ] . style = = Motion_HandCombat )
2018-03-29 17:01:57 -04:00
{
2018-07-20 18:19:00 -05:00
combatManeuver = & m_pWeenie - > _combatTable - > _cmt [ m ] ;
break ;
2018-03-29 17:01:57 -04:00
}
}
if ( combatManeuver )
{
* motion = combatManeuver - > motion ;
* height = combatManeuver - > attack_height ;
}
}
}
if ( ! * motion )
{
* motion = Motion_AttackHigh1 ;
}
}
void MonsterAIManager : : BeginReturningToSpawn ( )
{
2018-07-12 14:41:21 -07:00
// m_pWeenie->DoForcedStopCompletely();
2018-03-29 17:01:57 -04:00
MovementParameters params ;
params . can_walk = 0 ;
MovementStruct mvs ;
mvs . type = MovementTypes : : MoveToPosition ;
mvs . pos = m_HomePosition ;
mvs . params = & params ;
2020-08-20 13:34:57 -07:00
m_pWeenie - > PerformMovement ( mvs ) ;
2018-03-29 17:01:57 -04:00
m_fReturnTimeoutTime = Timer : : cur_time + m_fReturnTimeout ;
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChanceExecuteEmoteSet ( m_TargetID , Homesick_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
}
void MonsterAIManager : : EndReturningToSpawn ( )
{
}
void MonsterAIManager : : UpdateReturningToSpawn ( )
{
2018-07-12 14:41:21 -07:00
float fDistToHome = m_HomePosition . distance ( m_pWeenie - > m_Position ) ;
2018-03-29 17:01:57 -04:00
if ( fDistToHome < 5.0f )
{
SwitchState ( Idle ) ;
return ;
}
if ( m_fReturnTimeoutTime < = Timer : : cur_time )
{
// teleport back to spawn
2018-07-12 14:41:21 -07:00
m_pWeenie - > Movement_Teleport ( m_HomePosition ) ;
2018-03-29 17:01:57 -04:00
SwitchState ( Idle ) ;
return ;
}
}
void MonsterAIManager : : OnDeath ( )
{
}
2018-07-12 14:41:21 -07:00
bool MonsterAIManager : : IsValidTarget ( CWeenieObject * pWeenie )
2018-03-29 17:01:57 -04:00
{
if ( ! pWeenie )
return false ;
2018-07-12 14:41:21 -07:00
if ( pWeenie = = m_pWeenie )
2018-03-29 17:01:57 -04:00
return false ;
2019-05-01 21:14:45 +00:00
if ( ! pWeenie - > AsMonster ( ) )
return false ;
if ( ! pWeenie - > IsAttackable ( ) )
return false ;
if ( monster_brawl )
{
// if monster fighting is on, don't fight your own kind
return m_pWeenie - > InqIntQuality ( CREATURE_TYPE_INT , 0 ) ! = pWeenie - > InqIntQuality ( CREATURE_TYPE_INT , 0 ) ;
}
2019-05-13 12:34:28 -07:00
if ( m_pWeenie - > m_Qualities . m_WeenieType = = WeenieType : : Creature_WeenieType & &
2019-05-13 22:21:53 -07:00
( pWeenie - > m_Qualities . m_WeenieType = = WeenieType : : Creature_WeenieType & & ! pWeenie - > AsPlayer ( ) ) )
2019-05-13 12:34:28 -07:00
return false ;
2019-05-01 21:14:45 +00:00
// if a creature & not a player, don't fight
2019-05-11 19:26:01 -07:00
if ( m_pWeenie - > m_Qualities . m_WeenieType = = WeenieType : : CombatPet_WeenieType & &
( pWeenie - > AsPlayer ( ) | | pWeenie - > m_Qualities . m_WeenieType = = WeenieType : : CombatPet_WeenieType ) )
2018-03-29 17:01:57 -04:00
return false ;
2018-07-12 14:41:21 -07:00
if ( pWeenie - > ImmuneToDamage ( m_pWeenie ) ) // only attackable players (not dead, not in portal space, etc.
2018-03-29 17:01:57 -04:00
return false ;
return true ;
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : AlertIdleFriendsToAggro ( CWeenieObject * pAttacker )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
std : : list < CWeenieObject * > results ;
g_pWorld - > EnumNearby ( m_pWeenie , 20.0f , & results ) ;
2018-05-29 23:47:16 +01:00
2018-07-12 14:41:21 -07:00
int ourType = m_pWeenie - > InqIntQuality ( CREATURE_TYPE_INT , 0 ) ;
int ourFriendType = m_pWeenie - > InqIntQuality ( FRIEND_TYPE_INT , 0 ) ;
2018-03-29 17:01:57 -04:00
for ( auto weenie : results )
{
2018-07-12 14:41:21 -07:00
if ( weenie = = m_pWeenie )
2018-03-29 17:01:57 -04:00
continue ;
if ( ! weenie - > IsCreature ( ) )
continue ;
2018-07-12 14:41:21 -07:00
CMonsterWeenie * creature = ( CMonsterWeenie * ) weenie ;
2018-03-29 17:01:57 -04:00
if ( ! creature - > m_MonsterAI )
continue ;
switch ( creature - > m_MonsterAI - > m_State )
{
case Idle :
case ReturningToSpawn :
case SeekNewTarget :
break ;
default :
continue ;
}
if ( ! creature - > m_MonsterAI - > IsValidTarget ( pAttacker ) )
continue ;
if ( creature - > m_MonsterAI - > _toleranceType = = TolerateEverything )
continue ;
if ( creature - > m_MonsterAI - > _toleranceType = = TolerateUnlessAttacked & & _aiOptions ! = 1 ) // _aiOptions = 1 creatures do not tolerate their own kind/friends being attacked.
continue ;
int theirType = creature - > InqIntQuality ( CREATURE_TYPE_INT , 0 ) ;
int theirFriendType = creature - > InqIntQuality ( FRIEND_TYPE_INT , 0 ) ;
if ( ourType > 0 & & theirType > 0 )
{
if ( ourType ! = theirType & & ourType ! = theirFriendType & & theirType ! = ourFriendType ) //we only help our own kind or friends
continue ;
}
creature - > m_MonsterAI - > m_fAggroTime = Timer : : cur_time + 10.0 ;
creature - > m_MonsterAI - > SetNewTarget ( pAttacker ) ;
}
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : OnResistSpell ( CWeenieObject * attacker )
2018-03-29 17:01:57 -04:00
{
if ( attacker )
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChanceExecuteEmoteSet ( attacker - > GetID ( ) , ResistSpell_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : OnEvadeAttack ( CWeenieObject * attacker )
2018-03-29 17:01:57 -04:00
{
}
void MonsterAIManager : : OnDealtDamage ( DamageEventData & damageData )
{
if ( _nextTaunt > 0 & & _nextTaunt < = Timer : : cur_time )
{
2018-07-12 14:41:21 -07:00
if ( damageData . target )
m_pWeenie - > ChanceExecuteEmoteSet ( damageData . target - > GetID ( ) , Taunt_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
_nextTaunt = Timer : : cur_time + Random : : GenUInt ( 10 , 30 ) ;
}
}
void MonsterAIManager : : OnTookDamage ( DamageEventData & damageData )
{
2018-07-12 14:41:21 -07:00
CWeenieObject * source = damageData . source ;
unsigned int damage = damageData . outputDamageFinal ;
2018-03-29 17:01:57 -04:00
if ( ! source )
return ;
HandleAggro ( source ) ;
if ( damageData . wasCrit )
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChanceExecuteEmoteSet ( source - > GetID ( ) , ReceiveCritical_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > m_Qualities . _emote_table & & ! m_pWeenie - > IsExecutingEmote ( ) )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
PackableList < EmoteSet > * emoteSetList = m_pWeenie - > m_Qualities . _emote_table - > _emote_table . lookup ( WoundedTaunt_EmoteCategory ) ;
2018-03-29 17:01:57 -04:00
if ( emoteSetList )
{
2018-07-12 14:41:21 -07:00
double healthPercent = m_pWeenie - > GetHealthPercent ( ) ;
2018-03-29 17:01:57 -04:00
if ( m_fLastWoundedTauntHP > healthPercent )
{
double dice = Random : : GenFloat ( 0.0 , 1.0 ) ;
for ( auto & emoteSet : * emoteSetList )
{
// ignore probability?
if ( healthPercent > = emoteSet . minhealth & & healthPercent < emoteSet . maxhealth )
{
m_fLastWoundedTauntHP = healthPercent ;
2018-07-12 14:41:21 -07:00
m_pWeenie - > MakeEmoteManager ( ) - > ExecuteEmoteSet ( emoteSet , source - > GetID ( ) ) ;
2018-03-29 17:01:57 -04:00
}
}
}
}
}
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : OnIdentifyAttempted ( CWeenieObject * other )
2018-03-29 17:01:57 -04:00
{
if ( _toleranceType ! = TolerateUnlessBothered )
{
return ;
}
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > DistanceTo ( other , true ) > = 60.0 )
2018-03-29 17:01:57 -04:00
{
return ;
}
HandleAggro ( other ) ;
}
2018-07-12 14:41:21 -07:00
void MonsterAIManager : : HandleAggro ( CWeenieObject * pAttacker )
2018-03-29 17:01:57 -04:00
{
2020-09-04 17:06:39 -07:00
if ( ! pAttacker )
return ;
2018-03-29 17:01:57 -04:00
if ( _toleranceType = = TolerateEverything )
{
return ;
}
2020-07-08 19:53:24 +00:00
if ( ! pAttacker - > AsPlayer ( ) & & pAttacker - > AsMonster ( ) & & ! monster_brawl )
2020-07-07 13:39:25 -07:00
return ;
2018-07-12 14:41:21 -07:00
if ( ! m_pWeenie - > IsDead ( ) )
2018-03-29 17:01:57 -04:00
{
switch ( m_State )
{
case Idle :
case ReturningToSpawn :
case SeekNewTarget :
{
2020-07-07 13:39:25 -07:00
SetNewTarget ( pAttacker ) ;
m_pWeenie - > ChanceExecuteEmoteSet ( pAttacker - > GetID ( ) , Scream_EmoteCategory ) ;
m_fAggroTime = Timer : : cur_time + 10.0 ;
2018-03-29 17:01:57 -04:00
break ;
}
}
}
AlertIdleFriendsToAggro ( pAttacker ) ;
}
void MonsterAIManager : : BeginSeekNewTarget ( )
{
if ( ! ShouldSeekNewTarget ( ) | | ! SeekTarget ( ) )
{
SwitchState ( ReturningToSpawn ) ;
}
}
void MonsterAIManager : : UpdateSeekNewTarget ( )
{
SwitchState ( ReturningToSpawn ) ;
}
void MonsterAIManager : : EndSeekNewTarget ( )
{
}
void MonsterAIManager : : BeginMeleeModeAttack ( )
{
2018-07-12 14:41:21 -07:00
if ( _shield ! = NULL & & _currentShield = = NULL )
2018-05-29 23:47:16 +01:00
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > FinishMoveItemToWield ( _shield , SHIELD_LOC ) ) //make sure our shield is equipped
2018-03-29 17:01:57 -04:00
_currentShield = _shield ;
}
2018-07-12 14:41:21 -07:00
if ( _currentWeapon ! = _meleeWeapon )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
if ( _currentWeapon )
m_pWeenie - > FinishMoveItemToContainer ( _currentWeapon , m_pWeenie , 0 , true ) ;
if ( _meleeWeapon )
2018-05-29 23:47:16 +01:00
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > FinishMoveItemToWield ( _meleeWeapon , MELEE_WEAPON_LOC ) )
_currentWeapon = _meleeWeapon ;
2018-03-29 17:01:57 -04:00
else
2018-07-12 14:41:21 -07:00
_currentWeapon = 0 ;
2018-03-29 17:01:57 -04:00
}
}
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChangeCombatMode ( COMBAT_MODE : : MELEE_COMBAT_MODE , false ) ;
2018-03-29 17:01:57 -04:00
m_fChaseTimeoutTime = Timer : : cur_time + m_fChaseTimeoutDuration ;
m_fNextAttackTime = Timer : : cur_time ;
m_fNextChaseTime = Timer : : cur_time ;
m_fMinCombatStateTime = Timer : : cur_time + m_fMinCombatStateDuration ;
m_fMinReturnStateTime = Timer : : cur_time + m_fMinReturnStateDuration ;
}
void MonsterAIManager : : EndMeleeModeAttack ( )
{
2018-07-12 14:41:21 -07:00
m_pWeenie - > unstick_from_object ( ) ;
2018-03-29 17:01:57 -04:00
}
void MonsterAIManager : : UpdateMeleeModeAttack ( )
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > IsBusyOrInAction ( ) )
2018-03-29 17:01:57 -04:00
{
// still animating or busy (attacking, etc.)
return ;
}
// rules:
// dont switch targets to one that is farther than visual awareness range, unless attacked
// dont chase a target that is outside the chase range, unless attacked
// dont chase any new target, even if attacked, outside home range
2018-07-12 14:41:21 -07:00
CWeenieObject * pTarget = GetTargetWeenie ( ) ;
2019-05-29 17:04:25 -07:00
if ( ! pTarget | | pTarget - > IsDead ( ) | | ! pTarget - > IsAttackable ( ) | | pTarget - > ImmuneToDamage ( m_pWeenie ) | | m_pWeenie - > DistanceTo ( pTarget ) > = m_fChaseRange )
2018-03-29 17:01:57 -04:00
{
if ( ShouldSeekNewTarget ( ) )
{
SwitchState ( SeekNewTarget ) ;
}
else
{
SwitchState ( ReturningToSpawn ) ;
}
return ;
}
if ( DistanceToHome ( ) > = m_fMaxHomeRange )
{
SwitchState ( ReturningToSpawn ) ;
return ;
}
2018-07-12 14:41:21 -07:00
double fTargetDist = m_pWeenie - > DistanceTo ( pTarget , true ) ;
if ( fTargetDist > = m_pWeenie - > InqFloatQuality ( VISUAL_AWARENESS_RANGE_FLOAT , DEFAULT_AWARENESS_RANGE ) & & m_fAggroTime < = Timer : : cur_time )
2018-03-29 17:01:57 -04:00
{
SwitchState ( ReturningToSpawn ) ;
return ;
}
if ( m_fNextAttackTime > Timer : : cur_time )
{
return ;
}
2018-07-12 14:41:21 -07:00
if ( ! RollDiceCastSpell ( ) & & m_pWeenie - > DistanceTo ( pTarget ) < m_fChaseRange )
2018-03-29 17:01:57 -04:00
{
// do physics attack
2019-07-07 20:56:15 +00:00
uint32_t motion = 0 ;
2018-03-29 17:01:57 -04:00
ATTACK_HEIGHT height = ATTACK_HEIGHT : : UNDEF_ATTACK_HEIGHT ;
float power = 0.0f ;
GenerateRandomAttack ( & motion , & height , & power ) ;
2018-07-12 14:41:21 -07:00
m_pWeenie - > TryMeleeAttack ( pTarget - > GetID ( ) , height , power , motion ) ;
2018-03-29 17:01:57 -04:00
2018-07-20 18:19:00 -05:00
m_fNextAttackTime = Timer : : cur_time + 2.0f + power ;
2018-03-29 17:01:57 -04:00
m_fNextChaseTime = Timer : : cur_time ; // chase again anytime
m_fMinCombatStateTime = Timer : : cur_time + m_fMinCombatStateDuration ;
}
}
void MonsterAIManager : : BeginMissileModeAttack ( )
{
2018-07-12 14:41:21 -07:00
if ( _missileWeapon = = 0 )
2018-03-29 17:01:57 -04:00
{
SwitchState ( MeleeModeAttack ) ;
return ;
}
else
{
2018-07-12 14:41:21 -07:00
if ( _missileWeapon - > InqIntQuality ( DEFAULT_COMBAT_STYLE_INT , 0 ) ! = ThrownWeapon_CombatStyle )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
CWeenieObject * equippedAmmo = m_pWeenie - > GetWieldedCombat ( COMBAT_USE : : COMBAT_USE_AMMO ) ;
2018-03-29 17:01:57 -04:00
if ( ! equippedAmmo )
{
//we don't have ammo, disable missile mode and switch to melee.
2018-07-12 14:41:21 -07:00
_missileWeapon = 0 ;
2018-03-29 17:01:57 -04:00
SwitchState ( MeleeModeAttack ) ;
return ;
}
2018-07-12 14:41:21 -07:00
if ( _currentShield ! = NULL )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
m_pWeenie - > FinishMoveItemToContainer ( _currentShield , m_pWeenie , 0 , true ) ; //get rid of the shield.
_currentShield = NULL ;
2018-03-29 17:01:57 -04:00
}
}
2018-07-12 14:41:21 -07:00
else if ( _shield ! = NULL & & _currentShield = = NULL )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > FinishMoveItemToWield ( _shield , SHIELD_LOC ) ) //shields can be wielded with thrown weapons.
_currentShield = _shield ;
2018-03-29 17:01:57 -04:00
}
}
2018-07-12 14:41:21 -07:00
if ( _currentWeapon ! = _missileWeapon )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
if ( _currentWeapon )
2018-03-29 17:01:57 -04:00
{
2018-07-12 14:41:21 -07:00
m_pWeenie - > FinishMoveItemToContainer ( _currentWeapon , m_pWeenie , 0 , true ) ;
if ( m_pWeenie - > FinishMoveItemToWield ( _missileWeapon , MISSILE_WEAPON_LOC ) )
_currentWeapon = _missileWeapon ;
else
{
_currentWeapon = 0 ;
SwitchState ( MeleeModeAttack ) ;
}
2018-03-29 17:01:57 -04:00
}
}
2018-07-12 14:41:21 -07:00
m_pWeenie - > ChangeCombatMode ( COMBAT_MODE : : MISSILE_COMBAT_MODE , false ) ;
2018-03-29 17:01:57 -04:00
m_fChaseTimeoutTime = Timer : : cur_time + m_fChaseTimeoutDuration ;
m_fNextAttackTime = Timer : : cur_time ;
m_fNextChaseTime = Timer : : cur_time ;
m_fMinCombatStateTime = Timer : : cur_time + m_fMinCombatStateDuration ;
m_fMinReturnStateTime = Timer : : cur_time + m_fMinReturnStateDuration ;
}
void MonsterAIManager : : EndMissileModeAttack ( )
{
}
void MonsterAIManager : : UpdateMissileModeAttack ( )
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > IsBusyOrInAction ( ) | | m_pWeenie - > motions_pending ( ) )
2018-03-29 17:01:57 -04:00
{
// still animating or busy (attacking, etc.)
return ;
}
// rules:
// dont switch targets to one that is farther than visual awareness range, unless attacked
// dont chase a target that is outside the chase range, unless attacked
// dont chase any new target, even if attacked, outside home range
2018-07-12 14:41:21 -07:00
CWeenieObject * pTarget = GetTargetWeenie ( ) ;
2019-05-29 17:04:25 -07:00
if ( ! pTarget | | pTarget - > IsDead ( ) | | ! pTarget - > IsAttackable ( ) | | pTarget - > ImmuneToDamage ( m_pWeenie ) | | m_pWeenie - > DistanceTo ( pTarget ) > = m_fChaseRange )
2018-03-29 17:01:57 -04:00
{
if ( ShouldSeekNewTarget ( ) )
{
SwitchState ( SeekNewTarget ) ;
}
else
{
SwitchState ( ReturningToSpawn ) ;
}
return ;
}
if ( DistanceToHome ( ) > = m_fMaxHomeRange )
{
SwitchState ( ReturningToSpawn ) ;
return ;
}
float weaponMinRange = 1 ;
float weaponMaxRange = 60 ; //todo: get the value from the weapon? Players currently have 60 as a fixed max value.
2018-07-12 14:41:21 -07:00
double fTargetDist = m_pWeenie - > DistanceTo ( pTarget , true ) ;
2018-08-27 15:47:05 -05:00
if ( fTargetDist > = max ( m_pWeenie - > InqFloatQuality ( VISUAL_AWARENESS_RANGE_FLOAT , DEFAULT_AWARENESS_RANGE ) , ( double ) weaponMaxRange ) & & m_fAggroTime < = Timer : : cur_time )
2018-03-29 17:01:57 -04:00
{
SwitchState ( ReturningToSpawn ) ;
return ;
}
if ( m_fNextAttackTime > Timer : : cur_time )
{
return ;
}
2018-07-12 14:41:21 -07:00
if ( _meleeWeapon ! = NULL | | _hasUnarmedSkill ) //we also have a melee weapon(or know how to fight without one)
2018-03-29 17:01:57 -04:00
{
double roll = Random : : GenFloat ( 0.0 , 1.0 ) ;
if ( fTargetDist < weaponMinRange & & roll < 0.3 ) //the target is too close, let's go melee
{
SwitchState ( MeleeModeAttack ) ;
return ;
}
if ( roll < 0.02 ) //we're tired of doing missile attacks, let's switch it up
{
SwitchState ( MeleeModeAttack ) ;
return ;
}
}
if ( ! RollDiceCastSpell ( ) )
{
2018-07-12 14:41:21 -07:00
if ( m_pWeenie - > DistanceTo ( pTarget ) < weaponMaxRange )
2018-03-29 17:01:57 -04:00
{
// do physics attack
2019-07-07 20:56:15 +00:00
uint32_t motion = 0 ;
2018-03-29 17:01:57 -04:00
ATTACK_HEIGHT height = ( ATTACK_HEIGHT ) Random : : GenUInt ( 1 , 3 ) ;
2019-02-04 16:54:20 -06:00
float power = ( float ) Random : : GenFloat ( ) ;
2018-03-29 17:01:57 -04:00
2018-07-12 14:41:21 -07:00
m_pWeenie - > TryMissileAttack ( pTarget - > GetID ( ) , height , power ) ;
2018-03-29 17:01:57 -04:00
m_fNextAttackTime = Timer : : cur_time + 2.0f ;
m_fNextChaseTime = Timer : : cur_time ; // chase again anytime
m_fMinCombatStateTime = Timer : : cur_time + m_fMinCombatStateDuration ;
}
}
2019-07-07 20:56:15 +00:00
}