2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
Copyright ( C ) 2001 - 2002 EQEMu Development Team ( http : //eqemu.org)
2013-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY except by those people which sell it , which
2013-02-16 16:14:39 -08:00
are required to give you total support for your newly bought product ;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
2013-05-09 10:44:08 -04:00
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2013-02-16 16:14:39 -08:00
*/
2014-12-07 03:08:27 -06:00
2015-01-19 04:12:09 -06:00
# include "../common/global_define.h"
2015-01-10 15:26:38 -06:00
# include "../common/eqemu_logsys.h"
2013-02-23 13:45:19 -08:00
# include "../common/faction.h"
2013-02-16 16:14:39 -08:00
# include "../common/rulesys.h"
2014-12-07 03:08:27 -06:00
# include "../common/spdat.h"
# include "client.h"
# include "corpse.h"
# include "entity.h"
# include "mob.h"
# ifdef BOTS
# include "bot.h"
# endif
# include "map.h"
2020-04-02 11:55:05 -04:00
# include "water_map.h"
2013-02-16 16:14:39 -08:00
extern Zone * zone ;
//#define LOSDEBUG 6
void EntityList : : DescribeAggro ( Client * towho , NPC * from_who , float d , bool verbose ) {
float d2 = d * d ;
2013-05-06 13:07:41 -04:00
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " Describing aggro for %s " , from_who - > GetName ( ) ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
bool engaged = from_who - > IsEngaged ( ) ;
if ( engaged ) {
Mob * top = from_who - > GetHateTop ( ) ;
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " .. I am currently fighting with %s " , top = = nullptr ? " (nullptr) " : top - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
bool check_npcs = from_who - > WillAggroNPCs ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( verbose ) {
char namebuf [ 256 ] ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
int my_primary = from_who - > GetPrimaryFaction ( ) ;
Mob * own = from_who - > GetOwner ( ) ;
2013-05-04 18:06:58 -07:00
if ( own ! = nullptr )
2013-02-16 16:14:39 -08:00
my_primary = own - > GetPrimaryFaction ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( my_primary = = 0 ) {
strcpy ( namebuf , " (No faction) " ) ;
} else if ( my_primary < 0 ) {
strcpy ( namebuf , " (Special faction) " ) ;
} else {
if ( ! database . GetFactionName ( my_primary , namebuf , sizeof ( namebuf ) ) )
strcpy ( namebuf , " (Unknown) " ) ;
}
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " .. I am on faction %s (%d) \n " , namebuf , my_primary ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
for ( auto it = mob_list . begin ( ) ; it ! = mob_list . end ( ) ; + + it ) {
Mob * mob = it - > second ;
if ( mob - > IsClient ( ) ) //also ensures that mob != around
2013-02-16 16:14:39 -08:00
continue ;
2013-05-06 13:07:41 -04:00
2015-01-23 00:01:10 -08:00
if ( DistanceSquared ( mob - > GetPosition ( ) , from_who - > GetPosition ( ) ) > d2 )
2013-02-16 16:14:39 -08:00
continue ;
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
if ( engaged ) {
2013-02-16 16:14:39 -08:00
uint32 amm = from_who - > GetHateAmount ( mob ) ;
2014-02-10 10:39:12 -05:00
if ( amm = = 0 )
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ... %s is not on my hate list. " , mob - > GetName ( ) ) ;
2014-02-10 10:39:12 -05:00
else
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ... %s is on my hate list with value %lu " , mob - > GetName ( ) , ( unsigned long ) amm ) ;
2014-02-10 10:39:12 -05:00
} else if ( ! check_npcs & & mob - > IsNPC ( ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ... %s is an NPC and my npc_aggro is disabled. " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
} else {
from_who - > DescribeAggro ( towho , mob , verbose ) ;
}
}
}
void NPC : : DescribeAggro ( Client * towho , Mob * mob , bool verbose ) {
//this logic is duplicated from below, try to keep it up to date.
float iAggroRange = GetAggroRange ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
float t1 , t2 , t3 ;
2018-09-10 00:23:09 -07:00
t1 = std : : abs ( mob - > GetX ( ) - GetX ( ) ) ;
t2 = std : : abs ( mob - > GetY ( ) - GetY ( ) ) ;
t3 = std : : abs ( mob - > GetZ ( ) - GetZ ( ) ) ;
2013-05-09 10:44:08 -04:00
if ( ( t1 > iAggroRange )
| | ( t2 > iAggroRange )
| | ( t3 > iAggroRange ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is out of range (fast). distances (%.3f,%.3f,%.3f), range %.3f " , mob - > GetName ( ) ,
2013-05-09 10:44:08 -04:00
t1 , t2 , t3 , iAggroRange ) ;
return ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( mob - > IsInvisible ( this ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is invisible to me. " , mob - > GetName ( ) ) ;
2013-05-09 10:44:08 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
if ( ( mob - > IsClient ( ) & &
2013-05-09 10:44:08 -04:00
( ! mob - > CastToClient ( ) - > Connected ( )
| | mob - > CastToClient ( ) - > IsLD ( )
| | mob - > CastToClient ( ) - > IsBecomeNPC ( )
| | mob - > CastToClient ( ) - > GetGM ( )
)
) )
2013-02-16 16:14:39 -08:00
{
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is my owner. " , mob - > GetName ( ) ) ;
2013-05-09 10:44:08 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( mob = = GetOwner ( ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s a GM or is not connected. " , mob - > GetName ( ) ) ;
2013-05-09 10:44:08 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
2015-01-23 00:01:10 -08:00
float dist2 = DistanceSquared ( mob - > GetPosition ( ) , m_Position ) ;
2015-01-17 18:01:20 -08:00
2013-02-16 16:14:39 -08:00
float iAggroRange2 = iAggroRange * iAggroRange ;
if ( dist2 > iAggroRange2 ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is out of range. %.3f > %.3f " , mob - > GetName ( ) ,
2013-05-09 10:44:08 -04:00
dist2 , iAggroRange2 ) ;
return ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2016-08-04 20:33:29 -04:00
if ( RuleB ( Aggro , UseLevelAggro ) )
{
2020-03-05 22:31:54 -05:00
if ( GetLevel ( ) < RuleI ( Aggro , MinAggroLevel ) & & mob - > GetLevelCon ( GetLevel ( ) ) = = CON_GRAY & & GetBodyType ( ) ! = 3 & & ! AlwaysAggro ( ) )
2016-08-04 20:33:29 -04:00
{
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is red to me (basically) " , mob - > GetName ( ) , dist2 , iAggroRange2 ) ;
2016-08-04 20:33:29 -04:00
return ;
}
}
else
{
2020-03-05 22:31:54 -05:00
if ( GetINT ( ) > RuleI ( Aggro , IntAggroThreshold ) & & mob - > GetLevelCon ( GetLevel ( ) ) = = CON_GRAY & & ! AlwaysAggro ( ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is red to me (basically) " , mob - > GetName ( ) ,
2016-08-04 20:33:29 -04:00
dist2 , iAggroRange2 ) ;
return ;
}
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( verbose ) {
int my_primary = GetPrimaryFaction ( ) ;
int mob_primary = mob - > GetPrimaryFaction ( ) ;
Mob * own = GetOwner ( ) ;
2013-05-04 18:06:58 -07:00
if ( own ! = nullptr )
2013-02-16 16:14:39 -08:00
my_primary = own - > GetPrimaryFaction ( ) ;
own = mob - > GetOwner ( ) ;
2013-05-04 18:06:58 -07:00
if ( mob_primary > 0 & & own ! = nullptr )
2013-02-16 16:14:39 -08:00
mob_primary = own - > GetPrimaryFaction ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( mob_primary = = 0 ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s has no primary faction " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
} else if ( mob_primary < 0 ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is on special faction %d " , mob - > GetName ( ) , mob_primary ) ;
2013-02-16 16:14:39 -08:00
} else {
char namebuf [ 256 ] ;
if ( ! database . GetFactionName ( mob_primary , namebuf , sizeof ( namebuf ) ) )
strcpy ( namebuf , " (Unknown) " ) ;
2013-05-22 16:17:19 -04:00
std : : list < struct NPCFaction * > : : iterator cur , end ;
2013-02-16 16:14:39 -08:00
cur = faction_list . begin ( ) ;
end = faction_list . end ( ) ;
bool res = false ;
2014-01-13 22:14:02 -05:00
for ( ; cur ! = end ; + + cur ) {
2013-02-16 16:14:39 -08:00
struct NPCFaction * fac = * cur ;
if ( ( int32 ) fac - > factionID = = mob_primary ) {
if ( fac - > npc_value > 0 ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is on ALLY faction %s (%d) with %d " , mob - > GetName ( ) , namebuf , mob_primary , fac - > npc_value ) ;
2013-02-16 16:14:39 -08:00
res = true ;
break ;
} else if ( fac - > npc_value < 0 ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is on ENEMY faction %s (%d) with %d " , mob - > GetName ( ) , namebuf , mob_primary , fac - > npc_value ) ;
2013-02-16 16:14:39 -08:00
res = true ;
break ;
} else {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is on NEUTRAL faction %s (%d) with 0 " , mob - > GetName ( ) , namebuf , mob_primary ) ;
2013-02-16 16:14:39 -08:00
res = true ;
break ;
}
}
}
if ( ! res ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is on faction %s (%d), which I have no entry for. " , mob - > GetName ( ) , namebuf , mob_primary ) ;
2013-02-16 16:14:39 -08:00
}
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
FACTION_VALUE fv = mob - > GetReverseFactionCon ( this ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( ! (
fv = = FACTION_SCOWLS
| |
2013-05-04 18:06:58 -07:00
( mob - > GetPrimaryFaction ( ) ! = GetPrimaryFaction ( ) & & mob - > GetPrimaryFaction ( ) = = - 4 & & GetOwner ( ) = = nullptr )
2013-02-16 16:14:39 -08:00
| |
fv = = FACTION_THREATENLY
) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s faction not low enough. value='%s' " , mob - > GetName ( ) , FactionValueToString ( fv ) ) ;
2013-05-09 10:44:08 -04:00
return ;
2013-02-16 16:14:39 -08:00
}
if ( fv = = FACTION_THREATENLY ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s threatening to me, so they only have a %d chance per check of attacking. " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( ! CheckLosFN ( mob ) ) {
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s is out of sight. " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2019-08-11 00:27:04 -05:00
towho - > Message ( Chat : : White , " ...%s meets all conditions, I should be attacking them. " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
/*
If you change this function , you should update the above function
to keep the # aggro command accurate .
*/
bool Mob : : CheckWillAggro ( Mob * mob ) {
if ( ! mob )
return false ;
//sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM
//they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting
2013-05-06 13:07:41 -04:00
if ( mob - > IsClient ( ) ) {
2018-01-17 21:23:22 -05:00
if ( ! mob - > CastToClient ( ) - > ClientFinishedLoading ( ) | | mob - > CastToClient ( ) - > IsHoveringForRespawn ( ) | | mob - > CastToClient ( ) - > bZoning )
2013-03-09 13:18:36 +00:00
return false ;
}
2020-04-02 11:52:27 -04:00
// We don't want to aggro clients outside of water if we're water only.
if ( mob - > IsClient ( ) & & mob - > CastToClient ( ) - > GetLastRegion ( ) ! = RegionTypeWater & & IsUnderwaterOnly ( ) ) {
return false ;
}
2013-05-06 13:07:41 -04:00
2019-06-12 01:50:01 -05:00
/**
* Pets shouldn ' t scan for aggro
*/
if ( this - > GetOwner ( ) ) {
return false ;
}
2019-06-12 01:27:55 -05:00
Mob * pet_owner = mob - > GetOwner ( ) ;
if ( pet_owner & & pet_owner - > IsClient ( ) ) {
2013-02-16 16:14:39 -08:00
return false ;
2019-06-12 01:27:55 -05:00
}
2013-02-16 16:14:39 -08:00
float iAggroRange = GetAggroRange ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Check If it's invisible and if we can see invis
// Check if it's a client, and that the client is connected and not linkdead,
2013-05-09 10:44:08 -04:00
// and that the client isn't Playing an NPC, with thier gm flag on
2013-02-16 16:14:39 -08:00
// Check if it's not a Interactive NPC
// Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence.
// Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
float t1 , t2 , t3 ;
2018-09-10 00:23:09 -07:00
t1 = std : : abs ( mob - > GetX ( ) - GetX ( ) ) ;
t2 = std : : abs ( mob - > GetY ( ) - GetY ( ) ) ;
t3 = std : : abs ( mob - > GetZ ( ) - GetZ ( ) ) ;
2013-05-09 10:44:08 -04:00
if ( ( t1 > iAggroRange )
| | ( t2 > iAggroRange )
| | ( t3 > iAggroRange )
2017-03-28 01:30:42 -05:00
| | ( mob - > IsInvisible ( this ) )
2013-05-09 10:44:08 -04:00
| | ( mob - > IsClient ( ) & &
( ! mob - > CastToClient ( ) - > Connected ( )
| | mob - > CastToClient ( ) - > IsLD ( )
| | mob - > CastToClient ( ) - > IsBecomeNPC ( )
| | mob - > CastToClient ( ) - > GetGM ( )
)
) )
2013-02-16 16:14:39 -08:00
{
return ( false ) ;
}
2015-08-26 16:20:56 -04:00
// Don't aggro new clients if we are already engaged unless PROX_AGGRO is set
if ( IsEngaged ( ) & & ( ! GetSpecialAbility ( PROX_AGGRO ) | | ( GetSpecialAbility ( PROX_AGGRO ) & & ! CombatRange ( mob ) ) ) ) {
2019-09-02 03:18:12 -05:00
LogAggro ( " [{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}] " , GetName ( ) , mob - > GetName ( ) ) ;
2015-08-26 16:20:56 -04:00
return false ;
}
2013-02-16 16:14:39 -08:00
//im not sure I understand this..
//if I have an owner and it is not this mob, then I cannot
//aggro this mob...???
//changed to be 'if I have an owner and this is it'
if ( mob = = GetOwner ( ) ) {
return ( false ) ;
}
2015-01-23 00:01:10 -08:00
float dist2 = DistanceSquared ( mob - > GetPosition ( ) , m_Position ) ;
2013-02-16 16:14:39 -08:00
float iAggroRange2 = iAggroRange * iAggroRange ;
if ( dist2 > iAggroRange2 ) {
// Skip it, out of range
return ( false ) ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//Image: Get their current target and faction value now that its required
//this function call should seem backwards
FACTION_VALUE fv = mob - > GetReverseFactionCon ( this ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Make sure they're still in the zone
// Are they in range?
// Are they kos?
// Are we stupid or are they green
// and they don't have their gm flag on
int heroicCHA_mod = mob - > itembonuses . HeroicCHA / 25 ; // 800 Heroic CHA cap
if ( heroicCHA_mod > THREATENLY_ARRGO_CHANCE )
heroicCHA_mod = THREATENLY_ARRGO_CHANCE ;
2016-08-04 20:33:29 -04:00
if ( RuleB ( Aggro , UseLevelAggro ) & &
2013-02-16 16:14:39 -08:00
(
//old InZone check taken care of above by !mob->CastToClient()->Connected()
(
2018-10-05 16:50:23 -04:00
( GetLevel ( ) > = RuleI ( Aggro , MinAggroLevel ) )
2020-03-05 22:31:54 -05:00
| | ( GetBodyType ( ) = = 3 ) | | AlwaysAggro ( )
2013-02-16 16:14:39 -08:00
| | ( mob - > IsClient ( ) & & mob - > CastToClient ( ) - > IsSitting ( ) )
2017-03-13 15:07:56 -04:00
| | ( mob - > GetLevelCon ( GetLevel ( ) ) ! = CON_GRAY )
2013-02-16 16:14:39 -08:00
)
& &
(
(
fv = = FACTION_SCOWLS
| |
2013-05-04 18:06:58 -07:00
( mob - > GetPrimaryFaction ( ) ! = GetPrimaryFaction ( ) & & mob - > GetPrimaryFaction ( ) = = - 4 & & GetOwner ( ) = = nullptr )
2013-02-16 16:14:39 -08:00
| |
(
fv = = FACTION_THREATENLY
2014-12-01 18:13:12 -05:00
& & zone - > random . Roll ( THREATENLY_ARRGO_CHANCE - heroicCHA_mod )
2013-02-16 16:14:39 -08:00
)
)
)
)
2016-08-04 20:33:29 -04:00
)
2013-02-16 16:14:39 -08:00
{
//FatherNiwtit: make sure we can see them. last since it is very expensive
if ( CheckLosFN ( mob ) ) {
2019-09-02 03:18:12 -05:00
LogAggro ( " Check aggro for [{}] target [{}] " , GetName ( ) , mob - > GetName ( ) ) ;
2013-08-01 19:24:15 -05:00
return ( mod_will_aggro ( mob , this ) ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-09 10:44:08 -04:00
}
2016-08-04 20:33:29 -04:00
else
{
if
(
//old InZone check taken care of above by !mob->CastToClient()->Connected()
(
( GetINT ( ) < = RuleI ( Aggro , IntAggroThreshold ) )
2020-03-05 22:31:54 -05:00
| | AlwaysAggro ( )
2016-08-04 20:33:29 -04:00
| | ( mob - > IsClient ( ) & & mob - > CastToClient ( ) - > IsSitting ( ) )
2017-03-13 15:07:56 -04:00
| | ( mob - > GetLevelCon ( GetLevel ( ) ) ! = CON_GRAY )
2016-08-04 20:33:29 -04:00
)
& &
(
(
fv = = FACTION_SCOWLS
| |
( mob - > GetPrimaryFaction ( ) ! = GetPrimaryFaction ( ) & & mob - > GetPrimaryFaction ( ) = = - 4 & & GetOwner ( ) = = nullptr )
| |
(
fv = = FACTION_THREATENLY
& & zone - > random . Roll ( THREATENLY_ARRGO_CHANCE - heroicCHA_mod )
)
)
)
)
{
//FatherNiwtit: make sure we can see them. last since it is very expensive
if ( CheckLosFN ( mob ) ) {
2019-09-02 03:18:12 -05:00
LogAggro ( " Check aggro for [{}] target [{}] " , GetName ( ) , mob - > GetName ( ) ) ;
2016-08-04 20:33:29 -04:00
return ( mod_will_aggro ( mob , this ) ) ;
}
}
}
2015-01-16 02:07:19 -06:00
2019-09-02 03:18:12 -05:00
LogAggro ( " Is In zone?:[{}] \n " , mob - > InZone ( ) ) ;
LogAggro ( " Dist^2: [{}] \n " , dist2 ) ;
LogAggro ( " Range^2: [{}] \n " , iAggroRange2 ) ;
LogAggro ( " Faction: [{}] \n " , fv ) ;
2020-03-05 22:31:54 -05:00
LogAggro ( " AlwaysAggroFlag: [{}] \n " , AlwaysAggro ( ) ) ;
2019-09-02 03:18:12 -05:00
LogAggro ( " Int: [{}] \n " , GetINT ( ) ) ;
LogAggro ( " Con: [{}] \n " , GetLevelCon ( mob - > GetLevel ( ) ) ) ;
2015-01-16 02:07:19 -06:00
2013-02-16 16:14:39 -08:00
return ( false ) ;
}
2018-07-22 18:22:07 -04:00
int EntityList : : GetHatedCount ( Mob * attacker , Mob * exclude , bool inc_gray_con )
2014-02-10 10:39:12 -05:00
{
2013-02-16 16:14:39 -08:00
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
2014-02-10 10:39:12 -05:00
if ( ! attacker )
return 0 ;
2013-02-16 16:14:39 -08:00
int Count = 0 ;
2014-02-10 10:39:12 -05:00
for ( auto it = npc_list . begin ( ) ; it ! = npc_list . end ( ) ; + + it ) {
NPC * mob = it - > second ;
2018-07-22 18:22:07 -04:00
if ( ! mob | | ( mob = = exclude ) ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-05-06 13:07:41 -04:00
2018-07-22 18:22:07 -04:00
if ( ! mob - > IsEngaged ( ) ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-02-16 16:14:39 -08:00
2018-07-22 18:22:07 -04:00
if ( mob - > IsFeared ( ) | | mob - > IsMezzed ( ) ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-02-16 16:14:39 -08:00
2018-07-22 18:22:07 -04:00
if ( ! inc_gray_con & & attacker - > GetLevelCon ( mob - > GetLevel ( ) ) = = CON_GRAY ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-02-16 16:14:39 -08:00
2018-07-22 18:22:07 -04:00
if ( ! mob - > CheckAggro ( attacker ) ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-02-16 16:14:39 -08:00
float AggroRange = mob - > GetAggroRange ( ) ;
// Square it because we will be using DistNoRoot
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
AggroRange * = AggroRange ;
2013-02-16 16:14:39 -08:00
2018-07-22 18:22:07 -04:00
if ( DistanceSquared ( mob - > GetPosition ( ) , attacker - > GetPosition ( ) ) > AggroRange ) {
2014-02-10 10:39:12 -05:00
continue ;
2018-07-22 18:22:07 -04:00
}
2013-02-16 16:14:39 -08:00
Count + + ;
}
return Count ;
}
2019-12-25 03:16:14 -06:00
/**
* @ param target
* @ param isSpellAttack
* @ return
*/
2013-02-16 16:14:39 -08:00
bool Mob : : IsAttackAllowed ( Mob * target , bool isSpellAttack )
{
2017-03-23 04:35:36 -04:00
Mob * mob1 = nullptr , * mob2 = nullptr , * tempmob = nullptr ;
Client * c1 = nullptr , * c2 = nullptr , * becomenpc = nullptr ;
2013-02-16 16:14:39 -08:00
// NPC *npc1, *npc2;
int reverse ;
if ( ! zone - > CanDoCombat ( ) )
return false ;
// some special cases
if ( ! target )
return false ;
if ( this = = target ) // you can attack yourself
return true ;
2013-07-06 03:45:06 -07:00
if ( target - > GetSpecialAbility ( NO_HARM_FROM_CLIENT ) ) {
2013-02-16 16:14:39 -08:00
return false ;
}
// can't damage own pet (applies to everthing)
Mob * target_owner = target - > GetOwner ( ) ;
Mob * our_owner = GetOwner ( ) ;
if ( target_owner & & target_owner = = this )
return false ;
else if ( our_owner & & our_owner = = target )
return false ;
2013-05-06 13:07:41 -04:00
2014-03-05 23:02:57 -05:00
// invalidate for swarm pets for later on if their owner is a corpse
if ( IsNPC ( ) & & CastToNPC ( ) - > GetSwarmInfo ( ) & & our_owner & &
our_owner - > IsCorpse ( ) & & ! our_owner - > IsPlayerCorpse ( ) )
our_owner = nullptr ;
if ( target - > IsNPC ( ) & & target - > CastToNPC ( ) - > GetSwarmInfo ( ) & & target_owner & &
target_owner - > IsCorpse ( ) & & ! target_owner - > IsPlayerCorpse ( ) )
target_owner = nullptr ;
2013-02-16 16:14:39 -08:00
//cannot hurt untargetable mobs
bodyType bt = target - > GetBodyType ( ) ;
if ( bt = = BT_NoTarget | | bt = = BT_NoTarget2 ) {
if ( RuleB ( Pets , UnTargetableSwarmPet ) ) {
if ( target - > IsNPC ( ) ) {
if ( ! target - > CastToNPC ( ) - > GetSwarmOwner ( ) ) {
return ( false ) ;
}
} else {
return ( false ) ;
}
} else {
return ( false ) ;
}
}
if ( ! isSpellAttack )
{
if ( GetClass ( ) = = LDON_TREASURE )
{
return false ;
}
}
2013-05-06 13:07:41 -04:00
2015-01-31 17:03:44 -05:00
// the format here is a matrix of mob type vs mob type.
2013-02-16 16:14:39 -08:00
// redundant ones are omitted and the reverse is tried if it falls through.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
// first figure out if we're pets. we always look at the master's flags.
2013-02-16 16:14:39 -08:00
// no need to compare pets to anything
mob1 = our_owner ? our_owner : this ;
mob2 = target_owner ? target_owner : target ;
reverse = 0 ;
do
{
if ( _CLIENT ( mob1 ) )
{
if ( _CLIENT ( mob2 ) ) // client vs client
{
c1 = mob1 - > CastToClient ( ) ;
c2 = mob2 - > CastToClient ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if // if both are pvp they can fight
(
c1 - > GetPVP ( ) & &
c2 - > GetPVP ( )
)
return true ;
else if // if they're dueling they can go at it
(
c1 - > IsDueling ( ) & &
c2 - > IsDueling ( ) & &
c1 - > GetDuelTarget ( ) = = c2 - > GetID ( ) & &
c2 - > GetDuelTarget ( ) = = c1 - > GetID ( )
)
return true ;
else
return false ;
}
else if ( _NPC ( mob2 ) ) // client vs npc
2013-05-06 13:07:41 -04:00
{
2013-02-16 16:14:39 -08:00
return true ;
}
else if ( _BECOMENPC ( mob2 ) ) // client vs becomenpc
{
c1 = mob1 - > CastToClient ( ) ;
becomenpc = mob2 - > CastToClient ( ) ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if ( c1 - > GetLevel ( ) > becomenpc - > GetBecomeNPCLevel ( ) )
return false ;
else
return true ;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
else if ( _CLIENTCORPSE ( mob2 ) ) // client vs client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // client vs npc corpse
{
return false ;
}
}
else if ( _NPC ( mob1 ) )
{
if ( _NPC ( mob2 ) ) // npc vs npc
{
/*
this says that an NPC can NEVER attack a faction ally . . .
this is stupid . . . somebody else should check this rule if they want to
enforce it , this just says ' can they possibly fight based on their
type ' , in which case , the answer is yes .
*/
/* npc1 = mob1->CastToNPC();
npc2 = mob2 - > CastToNPC ( ) ;
if
(
npc1 - > GetPrimaryFaction ( ) ! = 0 & &
npc2 - > GetPrimaryFaction ( ) ! = 0 & &
(
npc1 - > GetPrimaryFaction ( ) = = npc2 - > GetPrimaryFaction ( ) | |
npc1 - > IsFactionListAlly ( npc2 - > GetPrimaryFaction ( ) )
)
)
return false ;
else
*/
return true ;
}
else if ( _BECOMENPC ( mob2 ) ) // npc vs becomenpc
{
return true ;
}
else if ( _CLIENTCORPSE ( mob2 ) ) // npc vs client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // npc vs npc corpse
{
return false ;
}
}
else if ( _BECOMENPC ( mob1 ) )
{
if ( _BECOMENPC ( mob2 ) ) // becomenpc vs becomenpc
{
return true ;
}
else if ( _CLIENTCORPSE ( mob2 ) ) // becomenpc vs client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // becomenpc vs npc corpse
{
return false ;
}
}
else if ( _CLIENTCORPSE ( mob1 ) )
{
if ( _CLIENTCORPSE ( mob2 ) ) // client corpse vs client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // client corpse vs npc corpse
{
return false ;
}
}
else if ( _NPCCORPSE ( mob1 ) )
{
if ( _NPCCORPSE ( mob2 ) ) // npc corpse vs npc corpse
{
return false ;
}
}
# ifdef BOTS
2019-06-21 06:00:58 -04:00
// this is HIGHLY inefficient
2013-02-16 16:14:39 -08:00
bool HasRuleDefined = false ;
bool IsBotAttackAllowed = false ;
IsBotAttackAllowed = Bot : : IsBotAttackAllowed ( mob1 , mob2 , HasRuleDefined ) ;
if ( HasRuleDefined )
return IsBotAttackAllowed ;
# endif //BOTS
// we fell through, now we swap the 2 mobs and run through again once more
tempmob = mob1 ;
mob1 = mob2 ;
mob2 = tempmob ;
}
while ( reverse + + = = 0 ) ;
2019-09-01 23:54:24 -05:00
LogDebug ( " Mob::IsAttackAllowed: don't have a rule for this - [{}] vs [{}] \n " , this - > GetName ( ) , target - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
return false ;
}
2015-01-31 17:03:44 -05:00
// this is to check if non detrimental things are allowed to be done
2013-05-09 10:44:08 -04:00
// to the target. clients cannot affect npcs and vice versa, and clients
2013-02-16 16:14:39 -08:00
// cannot affect other clients that are not of the same pvp flag as them.
// also goes for their pets
bool Mob : : IsBeneficialAllowed ( Mob * target )
{
2017-03-23 04:35:36 -04:00
Mob * mob1 = nullptr , * mob2 = nullptr , * tempmob = nullptr ;
Client * c1 = nullptr , * c2 = nullptr ;
2013-02-16 16:14:39 -08:00
int reverse ;
if ( ! target )
return false ;
if ( target - > GetAllowBeneficial ( ) )
return true ;
2015-01-31 17:03:44 -05:00
// see IsAttackAllowed for notes
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
// first figure out if we're pets. we always look at the master's flags.
2013-02-16 16:14:39 -08:00
// no need to compare pets to anything
mob1 = this - > GetOwnerID ( ) ? this - > GetOwner ( ) : this ;
mob2 = target - > GetOwnerID ( ) ? target - > GetOwner ( ) : target ;
// if it's self target or our own pet it's ok
if ( mob1 = = mob2 )
return true ;
reverse = 0 ;
do
{
if ( _CLIENT ( mob1 ) )
{
if ( _CLIENT ( mob2 ) ) // client to client
{
c1 = mob1 - > CastToClient ( ) ;
c2 = mob2 - > CastToClient ( ) ;
if ( c1 - > GetPVP ( ) = = c2 - > GetPVP ( ) )
return true ;
else if // if they're dueling they can heal each other too
(
c1 - > IsDueling ( ) & &
c2 - > IsDueling ( ) & &
c1 - > GetDuelTarget ( ) = = c2 - > GetID ( ) & &
c2 - > GetDuelTarget ( ) = = c1 - > GetID ( )
)
return true ;
else
return false ;
}
else if ( _NPC ( mob2 ) ) // client to npc
{
2013-05-09 10:44:08 -04:00
/* fall through and swap positions */
2013-02-16 16:14:39 -08:00
}
else if ( _BECOMENPC ( mob2 ) ) // client to becomenpc
{
return false ;
}
else if ( _CLIENTCORPSE ( mob2 ) ) // client to client corpse
{
return true ;
}
else if ( _NPCCORPSE ( mob2 ) ) // client to npc corpse
{
return false ;
}
# ifdef BOTS
else if ( mob2 - > IsBot ( ) )
return true ;
# endif
}
else if ( _NPC ( mob1 ) )
{
if ( _CLIENT ( mob2 ) )
{
return false ;
}
if ( _NPC ( mob2 ) ) // npc to npc
{
return true ;
}
else if ( _BECOMENPC ( mob2 ) ) // npc to becomenpc
{
return true ;
}
else if ( _CLIENTCORPSE ( mob2 ) ) // npc to client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // npc to npc corpse
{
return false ;
}
}
else if ( _BECOMENPC ( mob1 ) )
{
if ( _BECOMENPC ( mob2 ) ) // becomenpc to becomenpc
{
return true ;
}
else if ( _CLIENTCORPSE ( mob2 ) ) // becomenpc to client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // becomenpc to npc corpse
{
return false ;
}
}
else if ( _CLIENTCORPSE ( mob1 ) )
{
if ( _CLIENTCORPSE ( mob2 ) ) // client corpse to client corpse
{
return false ;
}
else if ( _NPCCORPSE ( mob2 ) ) // client corpse to npc corpse
{
return false ;
}
}
else if ( _NPCCORPSE ( mob1 ) )
{
if ( _NPCCORPSE ( mob2 ) ) // npc corpse to npc corpse
{
return false ;
}
}
// we fell through, now we swap the 2 mobs and run through again once more
tempmob = mob1 ;
mob1 = mob2 ;
mob2 = tempmob ;
}
while ( reverse + + = = 0 ) ;
2019-09-01 23:54:24 -05:00
LogDebug ( " Mob::IsBeneficialAllowed: don't have a rule for this - [{}] to [{}] \n " , this - > GetName ( ) , target - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
return false ;
}
bool Mob : : CombatRange ( Mob * other )
{
if ( ! other )
return ( false ) ;
float size_mod = GetSize ( ) ;
float other_size_mod = other - > GetSize ( ) ;
if ( GetRace ( ) = = 49 | | GetRace ( ) = = 158 | | GetRace ( ) = = 196 ) //For races with a fixed size
size_mod = 60.0f ;
else if ( size_mod < 6.0 )
size_mod = 8.0f ;
if ( other - > GetRace ( ) = = 49 | | other - > GetRace ( ) = = 158 | | other - > GetRace ( ) = = 196 ) //For races with a fixed size
other_size_mod = 60.0f ;
else if ( other_size_mod < 6.0 )
other_size_mod = 8.0f ;
if ( other_size_mod > size_mod )
{
size_mod = other_size_mod ;
}
// this could still use some work, but for now it's an improvement....
if ( size_mod > 29 )
size_mod * = size_mod ;
else if ( size_mod > 19 )
size_mod * = size_mod * 2 ;
else
size_mod * = size_mod * 4 ;
// prevention of ridiculously sized hit boxes
if ( size_mod > 10000 )
size_mod = size_mod / 7 ;
2013-05-06 13:07:41 -04:00
2018-05-20 17:24:00 -07:00
float _DistNoRoot = DistanceSquaredNoZ ( m_Position , other - > GetPosition ( ) ) ;
float _zDist = m_Position . z - other - > GetZ ( ) ;
_zDist * = _zDist ;
2014-08-23 03:21:36 -04:00
if ( GetSpecialAbility ( NPC_CHASE_DISTANCE ) ) {
2015-07-16 22:38:32 -04:00
2014-08-24 19:11:41 -04:00
bool DoLoSCheck = true ;
2014-08-23 03:21:36 -04:00
float max_dist = static_cast < float > ( GetSpecialAbilityParam ( NPC_CHASE_DISTANCE , 0 ) ) ;
float min_dist = static_cast < float > ( GetSpecialAbilityParam ( NPC_CHASE_DISTANCE , 1 ) ) ;
2015-07-16 22:38:32 -04:00
2014-11-02 20:14:44 -05:00
if ( GetSpecialAbilityParam ( NPC_CHASE_DISTANCE , 2 ) )
2014-08-24 19:11:41 -04:00
DoLoSCheck = false ; //Ignore line of sight check
2014-08-23 03:21:36 -04:00
if ( max_dist = = 1 )
max_dist = 250.0f ; //Default it to 250 if you forget to put a value
max_dist = max_dist * max_dist ;
if ( ! min_dist )
min_dist = size_mod ; //Default to melee range
else
min_dist = min_dist * min_dist ;
2015-07-16 22:38:32 -04:00
2014-08-24 19:11:41 -04:00
if ( ( DoLoSCheck & & CheckLastLosState ( ) ) & & ( _DistNoRoot > = min_dist & & _DistNoRoot < = max_dist ) )
2015-07-16 22:38:32 -04:00
SetPseudoRoot ( true ) ;
else
2014-08-23 03:21:36 -04:00
SetPseudoRoot ( false ) ;
}
if ( _DistNoRoot < = size_mod )
2013-02-16 16:14:39 -08:00
{
2018-05-20 17:24:00 -07:00
//A hack to kill an exploit till we get something better.
2018-11-09 00:54:51 -08:00
if ( flymode ! = GravityBehavior : : Flying & & _zDist > 500 & & ! CheckLastLosState ( ) ) {
2018-05-20 17:24:00 -07:00
return false ;
}
2013-02-16 16:14:39 -08:00
return true ;
}
return false ;
}
//Father Nitwit's LOS code
2019-08-16 03:25:34 -05:00
bool Mob : : CheckLosFN ( Mob * other )
{
2013-02-16 16:14:39 -08:00
bool Result = false ;
2019-08-16 03:25:34 -05:00
if ( other ) {
2013-02-16 16:14:39 -08:00
Result = CheckLosFN ( other - > GetX ( ) , other - > GetY ( ) , other - > GetZ ( ) , other - > GetSize ( ) ) ;
2019-08-16 03:25:34 -05:00
}
2013-02-16 16:14:39 -08:00
2014-08-23 03:21:36 -04:00
SetLastLosState ( Result ) ;
2015-07-16 22:38:32 -04:00
2013-02-16 16:14:39 -08:00
return Result ;
}
bool Mob : : CheckLosFN ( float posX , float posY , float posZ , float mobSize ) {
2013-05-04 18:06:58 -07:00
if ( zone - > zonemap = = nullptr ) {
2013-02-16 16:14:39 -08:00
//not sure what the best return is on error
//should make this a database variable, but im lazy today
# ifdef LOS_DEFAULT_CAN_SEE
return ( true ) ;
# else
return ( false ) ;
# endif
}
2013-05-06 13:07:41 -04:00
2015-01-23 00:01:10 -08:00
glm : : vec3 myloc ;
glm : : vec3 oloc ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
# define LOS_DEFAULT_HEIGHT 6.0f
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
myloc . x = GetX ( ) ;
myloc . y = GetY ( ) ;
myloc . z = GetZ ( ) + ( GetSize ( ) = = 0.0 ? LOS_DEFAULT_HEIGHT : GetSize ( ) ) / 2 * HEAD_POSITION ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
oloc . x = posX ;
oloc . y = posY ;
oloc . z = posZ + ( mobSize = = 0.0 ? LOS_DEFAULT_HEIGHT : mobSize ) / 2 * SEE_POSITION ;
# if LOSDEBUG>=5
2019-09-01 23:54:24 -05:00
LogDebug ( " LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}]) " , myloc . x , myloc . y , myloc . z , oloc . x , oloc . y , oloc . z , GetSize ( ) , mobSize ) ;
2013-02-16 16:14:39 -08:00
# endif
2014-02-23 21:17:28 -08:00
return zone - > zonemap - > CheckLoS ( myloc , oloc ) ;
2013-02-16 16:14:39 -08:00
}
2018-01-31 19:31:09 -05:00
bool Mob : : CheckLosFN ( glm : : vec3 posWatcher , float sizeWatcher , glm : : vec3 posTarget , float sizeTarget ) {
if ( zone - > zonemap = = nullptr ) {
//not sure what the best return is on error
//should make this a database variable, but im lazy today
# ifdef LOS_DEFAULT_CAN_SEE
return ( true ) ;
# else
return ( false ) ;
# endif
}
# define LOS_DEFAULT_HEIGHT 6.0f
posWatcher . z + = ( sizeWatcher = = 0.0f ? LOS_DEFAULT_HEIGHT : sizeWatcher ) / 2 * HEAD_POSITION ;
posTarget . z + = ( sizeTarget = = 0.0f ? LOS_DEFAULT_HEIGHT : sizeTarget ) / 2 * SEE_POSITION ;
# if LOSDEBUG>=5
2019-09-01 23:54:24 -05:00
LogDebug ( " LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}]) [static] " , posWatcher . x , posWatcher . y , posWatcher . z , posTarget . x , posTarget . y , posTarget . z , sizeWatcher , sizeTarget ) ;
2018-01-31 19:31:09 -05:00
# endif
return zone - > zonemap - > CheckLoS ( posWatcher , posTarget ) ;
}
2013-02-16 16:14:39 -08:00
//offensive spell aggro
2015-07-16 22:38:32 -04:00
int32 Mob : : CheckAggroAmount ( uint16 spell_id , Mob * target , bool isproc )
2014-01-24 15:28:54 -05:00
{
2015-10-08 16:05:14 -04:00
if ( NoDetrimentalSpellAggro ( spell_id ) )
return 0 ;
2013-02-16 16:14:39 -08:00
int32 AggroAmount = 0 ;
int32 nonModifiedAggro = 0 ;
uint16 slevel = GetLevel ( ) ;
2015-07-16 22:38:32 -04:00
bool dispel = false ;
bool on_hatelist = target ? target - > CheckAggro ( this ) : false ;
2015-07-22 16:31:38 -04:00
int proc_cap = RuleI ( Aggro , MaxScalingProcAggro ) ;
int hate_cap = isproc & & proc_cap ! = - 1 ? proc_cap : 1200 ;
2015-07-16 22:38:32 -04:00
int32 target_hp = target ? target - > GetMaxHP ( ) : 18000 ; // default to max
2015-07-22 16:31:38 -04:00
int32 default_aggro = 25 ;
2015-07-16 22:38:32 -04:00
if ( target_hp > = 18000 ) // max
2015-07-22 16:31:38 -04:00
default_aggro = hate_cap ;
else if ( target_hp > = 390 ) // min, 390 is the first number with int division that is 26
2015-07-16 22:38:32 -04:00
default_aggro = target_hp / 15 ;
2013-02-16 16:14:39 -08:00
for ( int o = 0 ; o < EFFECT_COUNT ; o + + ) {
2014-01-24 15:28:54 -05:00
switch ( spells [ spell_id ] . effectid [ o ] ) {
2013-02-16 16:14:39 -08:00
case SE_CurrentHPOnce :
2014-01-24 15:28:54 -05:00
case SE_CurrentHP : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
if ( val < 0 )
AggroAmount - = val ;
break ;
}
2013-02-16 16:14:39 -08:00
case SE_MovementSpeed : {
2014-01-24 15:28:54 -05:00
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
if ( val < 0 )
2015-07-22 16:31:38 -04:00
AggroAmount + = default_aggro ;
2013-02-16 16:14:39 -08:00
break ;
}
case SE_AttackSpeed :
case SE_AttackSpeed2 :
2014-01-24 15:28:54 -05:00
case SE_AttackSpeed3 : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
if ( val < 100 )
2015-07-22 16:31:38 -04:00
AggroAmount + = default_aggro ;
2013-02-16 16:14:39 -08:00
break ;
}
2015-07-16 22:38:32 -04:00
case SE_Stun :
case SE_Blind :
case SE_Mez :
case SE_Charm :
case SE_Fear :
2015-07-22 16:31:38 -04:00
AggroAmount + = default_aggro ;
2013-02-16 16:14:39 -08:00
break ;
2015-07-16 22:38:32 -04:00
case SE_Root :
AggroAmount + = 10 ;
2013-02-16 16:14:39 -08:00
break ;
case SE_ACv2 :
2014-01-24 15:28:54 -05:00
case SE_ArmorClass : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
if ( val < 0 )
2015-07-22 16:31:38 -04:00
AggroAmount + = default_aggro ;
2013-02-16 16:14:39 -08:00
break ;
}
2015-07-22 16:31:38 -04:00
case SE_ATK :
2013-02-16 16:14:39 -08:00
case SE_ResistMagic :
case SE_ResistFire :
case SE_ResistCold :
case SE_ResistPoison :
2015-07-16 22:38:32 -04:00
case SE_ResistDisease :
2013-02-16 16:14:39 -08:00
case SE_STR :
case SE_STA :
case SE_DEX :
case SE_AGI :
case SE_INT :
case SE_WIS :
2014-01-24 15:28:54 -05:00
case SE_CHA : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
if ( val < 0 )
2015-07-16 22:38:32 -04:00
AggroAmount + = 10 ;
2013-02-16 16:14:39 -08:00
break ;
}
2015-07-16 22:38:32 -04:00
case SE_ResistAll : {
2014-01-24 15:28:54 -05:00
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
if ( val < 0 )
2015-07-16 22:38:32 -04:00
AggroAmount + = 50 ;
2014-01-24 15:28:54 -05:00
break ;
2013-02-16 16:14:39 -08:00
}
2015-07-16 22:38:32 -04:00
case SE_AllStats : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
if ( val < 0 )
AggroAmount + = 70 ;
2014-01-24 15:28:54 -05:00
break ;
2013-02-16 16:14:39 -08:00
}
2015-07-16 22:38:32 -04:00
case SE_BardAEDot :
AggroAmount + = 10 ;
2014-01-24 15:28:54 -05:00
break ;
2015-07-16 22:38:32 -04:00
case SE_SpinTarget :
2013-02-16 16:14:39 -08:00
case SE_Amnesia :
2015-07-16 22:38:32 -04:00
case SE_Silence :
case SE_Destroy :
2015-07-22 16:31:38 -04:00
AggroAmount + = default_aggro ;
2013-02-16 16:14:39 -08:00
break ;
2015-07-16 22:38:32 -04:00
// unsure -- leave them this for now
2013-02-16 16:14:39 -08:00
case SE_Harmony :
case SE_CastingLevel :
case SE_MeleeMitigation :
case SE_CriticalHitChance :
case SE_AvoidMeleeChance :
case SE_RiposteChance :
case SE_DodgeChance :
case SE_ParryChance :
case SE_DualWieldChance :
case SE_DoubleAttackChance :
case SE_MeleeSkillCheck :
case SE_HitChance :
case SE_DamageModifier :
case SE_MinDamageModifier :
case SE_IncreaseBlockChance :
case SE_Accuracy :
case SE_DamageShield :
case SE_SpellDamageShield :
2014-01-24 15:28:54 -05:00
case SE_ReverseDS : {
AggroAmount + = slevel * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
2015-07-16 22:38:32 -04:00
// unsure -- leave them this for now
2013-02-16 16:14:39 -08:00
case SE_CurrentMana :
case SE_ManaRegen_v2 :
case SE_ManaPool :
2014-01-24 15:28:54 -05:00
case SE_CurrentEndurance : {
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
if ( val < 0 )
2014-01-24 15:28:54 -05:00
AggroAmount - = val * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
case SE_CancelMagic :
2015-07-16 22:38:32 -04:00
case SE_DispelDetrimental :
dispel = true ;
2013-02-16 16:14:39 -08:00
break ;
case SE_ReduceHate :
2015-07-16 22:38:32 -04:00
case SE_InstantHate :
2014-01-24 15:28:54 -05:00
nonModifiedAggro = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , slevel , spell_id ) ;
2013-02-16 16:14:39 -08:00
break ;
}
}
2015-07-22 16:31:38 -04:00
if ( IsBardSong ( spell_id ) & & AggroAmount > 40 )
AggroAmount = 40 ; // bard songs seem to cap to 40 for most of their spells?
2015-07-16 22:38:32 -04:00
if ( dispel & & target & & target - > GetHateAmount ( this ) < 100 )
AggroAmount + = 50 ;
2013-02-16 16:14:39 -08:00
2015-07-16 22:38:32 -04:00
if ( spells [ spell_id ] . HateAdded > 0 ) // overrides the hate (ex. tash)
2013-02-16 16:14:39 -08:00
AggroAmount = spells [ spell_id ] . HateAdded ;
if ( GetOwner ( ) & & IsPet ( ) )
AggroAmount = AggroAmount * RuleI ( Aggro , PetSpellAggroMod ) / 100 ;
2013-05-06 13:07:41 -04:00
2015-07-22 16:31:38 -04:00
// hate focus ignored on first action for some reason
if ( ! on_hatelist & & AggroAmount > 0 ) {
2013-02-16 16:14:39 -08:00
int HateMod = RuleI ( Aggro , SpellAggroMod ) ;
2014-12-08 01:22:01 -05:00
HateMod + = GetFocusEffect ( focusSpellHateMod , spell_id ) ;
2013-02-16 16:14:39 -08:00
AggroAmount = ( AggroAmount * HateMod ) / 100 ;
}
2015-07-22 16:31:38 -04:00
// initial aggro gets a bonus 100 besides for dispel or hate override
2015-09-17 01:46:40 -04:00
// We add this 100 in AddToHateList so we need to account for the oddities here
if ( dispel & & spells [ spell_id ] . HateAdded > 0 & & ! on_hatelist )
AggroAmount - = 100 ;
2015-07-16 22:38:32 -04:00
2014-01-24 15:28:54 -05:00
return AggroAmount + spells [ spell_id ] . bonushate + nonModifiedAggro ;
2013-02-16 16:14:39 -08:00
}
//healing and buffing aggro
2015-07-16 22:38:32 -04:00
int32 Mob : : CheckHealAggroAmount ( uint16 spell_id , Mob * target , uint32 heal_possible )
2014-01-24 15:28:54 -05:00
{
2013-02-16 16:14:39 -08:00
int32 AggroAmount = 0 ;
2015-07-18 01:01:27 -04:00
auto target_level = target ? target - > GetLevel ( ) : 1 ;
2015-07-16 22:38:32 -04:00
bool ignore_default_buff = false ; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default
2013-02-16 16:14:39 -08:00
for ( int o = 0 ; o < EFFECT_COUNT ; o + + ) {
2014-01-24 15:28:54 -05:00
switch ( spells [ spell_id ] . effectid [ o ] ) {
2015-07-16 22:38:32 -04:00
case SE_CurrentHP : {
if ( heal_possible = = 0 ) {
AggroAmount + = 1 ;
2013-02-16 16:14:39 -08:00
break ;
}
2015-07-16 22:38:32 -04:00
// hate based on base healing power of the spell
int val = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] ,
spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , GetLevel ( ) , spell_id ) ;
if ( val > 0 ) {
if ( heal_possible < val )
val = heal_possible ; // capped to amount healed
val = 2 * val / 3 ; // 3:2 ratio
if ( target_level > 50 & & val > 1500 )
val = 1500 ; // target 51+ seems ~1500
else if ( target_level < = 50 & & val > 800 )
val = 800 ; // per live patch notes, capped to 800
2013-05-09 10:44:08 -04:00
}
2015-07-16 22:38:32 -04:00
AggroAmount + = std : : max ( val , 1 ) ;
break ;
}
case SE_Rune :
2015-07-22 16:31:38 -04:00
AggroAmount + = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] ,
spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , GetLevel ( ) , spell_id ) * 2 ;
2015-07-16 22:38:32 -04:00
ignore_default_buff = true ;
break ;
case SE_HealOverTime :
AggroAmount + = 10 ;
ignore_default_buff = true ;
break ;
default :
break ;
2013-02-16 16:14:39 -08:00
}
}
if ( GetOwner ( ) & & IsPet ( ) )
AggroAmount = AggroAmount * RuleI ( Aggro , PetSpellAggroMod ) / 100 ;
2015-07-16 22:38:32 -04:00
if ( ! ignore_default_buff & & IsBuffSpell ( spell_id ) & & IsBeneficialSpell ( spell_id ) )
2015-07-18 01:01:27 -04:00
AggroAmount = IsBardSong ( spell_id ) ? 2 : 9 ;
2015-07-16 22:38:32 -04:00
2014-01-24 15:28:54 -05:00
if ( AggroAmount > 0 ) {
2013-02-16 16:14:39 -08:00
int HateMod = RuleI ( Aggro , SpellAggroMod ) ;
2014-12-08 01:22:01 -05:00
HateMod + = GetFocusEffect ( focusSpellHateMod , spell_id ) ;
2013-02-16 16:14:39 -08:00
AggroAmount = ( AggroAmount * HateMod ) / 100 ;
}
2015-07-22 16:31:38 -04:00
return std : : max ( 0 , AggroAmount ) ;
2013-02-16 16:14:39 -08:00
}
void Mob : : AddFeignMemory ( Client * attacker ) {
2015-11-01 17:12:14 -06:00
if ( feign_memory_list . empty ( ) & & AI_feign_remember_timer ! = nullptr )
AI_feign_remember_timer - > Start ( AIfeignremember_delay ) ;
2013-02-16 16:14:39 -08:00
feign_memory_list . insert ( attacker - > CharacterID ( ) ) ;
}
void Mob : : RemoveFromFeignMemory ( Client * attacker ) {
feign_memory_list . erase ( attacker - > CharacterID ( ) ) ;
2015-11-01 17:12:14 -06:00
if ( feign_memory_list . empty ( ) & & AI_feign_remember_timer ! = nullptr )
AI_feign_remember_timer - > Disable ( ) ;
2013-02-16 16:14:39 -08:00
if ( feign_memory_list . empty ( ) )
{
minLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMin ) ;
maxLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMax ) ;
2015-11-01 17:12:14 -06:00
if ( AI_feign_remember_timer ! = nullptr )
AI_feign_remember_timer - > Disable ( ) ;
2013-02-16 16:14:39 -08:00
}
}
void Mob : : ClearFeignMemory ( ) {
2016-05-25 16:10:28 -04:00
auto RememberedCharID = feign_memory_list . begin ( ) ;
2013-05-09 10:44:08 -04:00
while ( RememberedCharID ! = feign_memory_list . end ( ) )
{
Client * remember_client = entity_list . GetClientByCharID ( * RememberedCharID ) ;
if ( remember_client ! = nullptr ) //Still in zone
remember_client - > RemoveXTarget ( this , false ) ;
+ + RememberedCharID ;
}
2013-02-22 22:27:03 -05:00
2013-02-16 16:14:39 -08:00
feign_memory_list . clear ( ) ;
minLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMin ) ;
maxLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMax ) ;
2015-11-01 17:12:14 -06:00
if ( AI_feign_remember_timer ! = nullptr )
AI_feign_remember_timer - > Disable ( ) ;
2013-02-16 16:14:39 -08:00
}
2017-10-16 01:25:14 -04:00
bool Mob : : IsOnFeignMemory ( Client * attacker ) const
{
return feign_memory_list . find ( attacker - > CharacterID ( ) ) ! = feign_memory_list . end ( ) ;
}
2015-01-30 18:47:27 -05:00
bool Mob : : PassCharismaCheck ( Mob * caster , uint16 spell_id ) {
2013-02-16 16:14:39 -08:00
2014-03-02 03:46:51 -05:00
/*
Charm formula is correct based on over 50 hours of personal live parsing - Kayen
2014-03-20 00:53:49 -04:00
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = - 1 Resist mod up to 255 CHA ( min ~ 75 CHA )
2014-03-02 03:46:51 -05:00
Charisma DOES NOT extend charm durations .
Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate ( unclear if does anything )
2014-03-02 19:44:42 -05:00
Charm has a lower limit of 5 % chance to break per tick , regardless of resist modifiers / level difference .
2014-03-02 03:46:51 -05:00
*/
2013-02-16 16:14:39 -08:00
if ( ! caster ) return false ;
if ( spells [ spell_id ] . ResistDiff < = - 600 )
return true ;
2014-03-02 03:46:51 -05:00
float resist_check = 0 ;
2015-07-16 22:38:32 -04:00
2013-02-16 16:14:39 -08:00
if ( IsCharmSpell ( spell_id ) ) {
2015-10-08 11:49:21 -04:00
if ( spells [ spell_id ] . no_resist ) //If charm spell has this set(-1), it can not break till end of duration.
2013-02-16 16:14:39 -08:00
return true ;
//1: The mob has a default 25% chance of being allowed a resistance check against the charm.
2014-12-01 18:13:12 -05:00
if ( zone - > random . Int ( 0 , 99 ) > RuleI ( Spells , CharmBreakCheckChance ) )
2013-02-16 16:14:39 -08:00
return true ;
2014-03-02 19:44:42 -05:00
if ( RuleB ( Spells , CharismaCharmDuration ) )
2015-01-30 18:47:27 -05:00
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , false , 0 , true , true ) ;
2014-03-02 19:44:42 -05:00
else
2015-01-30 18:47:27 -05:00
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , false , 0 , false , true ) ;
2014-03-02 19:44:42 -05:00
2013-02-16 16:14:39 -08:00
//2: The mob makes a resistance check against the charm
2015-07-16 22:38:32 -04:00
if ( resist_check = = 100 )
2013-05-06 13:07:41 -04:00
return true ;
2013-02-16 16:14:39 -08:00
else
{
if ( caster - > IsClient ( ) )
{
2013-05-06 13:07:41 -04:00
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred.
2014-02-26 00:09:59 -05:00
int16 TotalDominationBonus = caster - > aabonuses . CharmBreakChance + caster - > spellbonuses . CharmBreakChance + caster - > itembonuses . CharmBreakChance ;
2013-05-06 13:07:41 -04:00
2014-12-01 18:13:12 -05:00
if ( zone - > random . Int ( 0 , 99 ) < TotalDominationBonus )
2013-02-16 16:14:39 -08:00
return true ;
}
}
}
else
{
// Assume this is a harmony/pacify spell
2014-03-08 05:35:22 -05:00
// If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro.
2015-01-30 18:47:27 -05:00
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , false , 0 , true ) ;
2013-02-16 16:14:39 -08:00
if ( resist_check = = 100 )
2013-05-06 13:07:41 -04:00
return true ;
2013-02-16 16:14:39 -08:00
}
return false ;
}
2014-03-05 01:34:17 -05:00
void Mob : : RogueEvade ( Mob * other )
{
2016-11-16 02:36:20 -05:00
int amount = other - > GetHateAmount ( this ) * zone - > random . Int ( 40 , 70 ) / 100 ;
other - > SetHateAmountOnEnt ( this , std : : max ( 100 , amount ) ) ;
2014-03-05 01:34:17 -05:00
return ;
}