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
*/
# include "../common/debug.h"
# include <stdlib.h>
# include <math.h>
# include "masterentity.h"
2013-02-23 13:45:19 -08:00
# include "../common/faction.h"
2013-02-16 16:14:39 -08:00
# include "map.h"
2013-02-19 15:33:21 -08:00
# include "../common/spdat.h"
2013-02-16 16:14:39 -08:00
# include "../common/skills.h"
2014-08-21 19:33:02 -07:00
# include "../common/misc_functions.h"
2013-02-16 16:14:39 -08:00
# include "../common/rulesys.h"
2014-08-21 23:46:01 -07:00
# include "string_ids.h"
2013-02-16 16:14:39 -08:00
# include <iostream>
extern Zone * zone ;
//#define LOSDEBUG 6
//look around a client for things which might aggro the client.
2014-02-10 10:39:12 -05:00
void EntityList : : CheckClientAggro ( Client * around )
{
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
2014-02-10 10:39:12 -05:00
if ( mob - > CheckWillAggro ( around ) ) {
if ( mob - > IsEngaged ( ) )
2013-02-16 16:14:39 -08:00
mob - > AddToHateList ( around ) ;
else
mob - > AddToHateList ( around , mob - > GetLevel ( ) ) ;
}
}
}
void EntityList : : DescribeAggro ( Client * towho , NPC * from_who , float d , bool verbose ) {
float d2 = d * d ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
towho - > Message ( 0 , " 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 ( ) ;
2013-05-04 18:06:58 -07:00
towho - > Message ( 0 , " .. 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) " ) ;
}
towho - > Message ( 0 , " .. I am on faction %s (%d) \n " , namebuf , my_primary ) ;
}
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
2014-02-10 10:39:12 -05:00
if ( mob - > DistNoRoot ( * from_who ) > 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 )
2013-02-16 16:14:39 -08:00
towho - > Message ( 0 , " ... %s is not on my hate list. " , mob - > GetName ( ) ) ;
2014-02-10 10:39:12 -05:00
else
2013-02-16 16:14:39 -08:00
towho - > Message ( 0 , " ... %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 ( ) ) {
2013-02-16 16:14:39 -08:00
towho - > Message ( 0 , " ... %s is an NPC and my npc_aggro is disabled. " , mob - > GetName ( ) ) ;
} 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 ;
t1 = mob - > GetX ( ) - GetX ( ) ;
t2 = mob - > GetY ( ) - GetY ( ) ;
t3 = mob - > GetZ ( ) - GetZ ( ) ;
//Cheap ABS()
if ( t1 < 0 )
t1 = 0 - t1 ;
if ( t2 < 0 )
t2 = 0 - t2 ;
if ( t3 < 0 )
t3 = 0 - t3 ;
2013-05-09 10:44:08 -04:00
if ( ( t1 > iAggroRange )
| | ( t2 > iAggroRange )
| | ( t3 > iAggroRange ) ) {
towho - > Message ( 0 , " ...%s is out of range (fast). distances (%.3f,%.3f,%.3f), range %.3f " , mob - > GetName ( ) ,
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 ) ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s is invisible to me. " , mob - > GetName ( ) ) ;
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
{
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s is my owner. " , mob - > GetName ( ) ) ;
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 ( ) ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s a GM or is not connected. " , mob - > GetName ( ) ) ;
return ;
2013-02-16 16:14:39 -08:00
}
2013-05-09 10:44:08 -04:00
float dist2 = mob - > DistNoRoot ( * this ) ;
2013-02-16 16:14:39 -08:00
float iAggroRange2 = iAggroRange * iAggroRange ;
if ( dist2 > iAggroRange2 ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s is out of range. %.3f > %.3f " , mob - > GetName ( ) ,
dist2 , iAggroRange2 ) ;
return ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-08-01 19:24:15 -05:00
if ( GetINT ( ) > RuleI ( Aggro , IntAggroThreshold ) & & mob - > GetLevelCon ( GetLevel ( ) ) = = CON_GREEN ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s is red to me (basically) " , mob - > GetName ( ) ,
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 ) {
towho - > Message ( 0 , " ...%s has no primary faction " , mob - > GetName ( ) ) ;
} else if ( mob_primary < 0 ) {
towho - > Message ( 0 , " ...%s is on special faction %d " , mob - > GetName ( ) , mob_primary ) ;
} 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 ) {
towho - > Message ( 0 , " ...%s is on ALLY faction %s (%d) with %d " , mob - > GetName ( ) , namebuf , mob_primary , fac - > npc_value ) ;
res = true ;
break ;
} else if ( fac - > npc_value < 0 ) {
towho - > Message ( 0 , " ...%s is on ENEMY faction %s (%d) with %d " , mob - > GetName ( ) , namebuf , mob_primary , fac - > npc_value ) ;
res = true ;
break ;
} else {
towho - > Message ( 0 , " ...%s is on NEUTRAL faction %s (%d) with 0 " , mob - > GetName ( ) , namebuf , mob_primary ) ;
res = true ;
break ;
}
}
}
if ( ! res ) {
towho - > Message ( 0 , " ...%s is on faction %s (%d), which I have no entry for. " , mob - > GetName ( ) , namebuf , mob_primary ) ;
}
}
}
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
) ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s faction not low enough. value='%s' " , mob - > GetName ( ) , FactionValueToString ( fv ) ) ;
return ;
2013-02-16 16:14:39 -08:00
}
if ( fv = = FACTION_THREATENLY ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%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 ) ) {
2013-05-09 10:44:08 -04:00
towho - > Message ( 0 , " ...%s is out of sight. " , mob - > GetName ( ) ) ;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
towho - > Message ( 0 , " ...%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 ( ) ) {
2013-03-09 13:18:36 +00:00
if ( ! mob - > CastToClient ( ) - > ClientFinishedLoading ( ) | | mob - > CastToClient ( ) - > IsHoveringForRespawn ( ) )
return false ;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Mob * ownr = mob - > GetOwner ( ) ;
if ( ownr & & ownr - > IsClient ( ) & & ! ownr - > CastToClient ( ) - > ClientFinishedLoading ( ) )
return false ;
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 ;
t1 = mob - > GetX ( ) - GetX ( ) ;
t2 = mob - > GetY ( ) - GetY ( ) ;
t3 = mob - > GetZ ( ) - GetZ ( ) ;
//Cheap ABS()
if ( t1 < 0 )
t1 = 0 - t1 ;
if ( t2 < 0 )
t2 = 0 - t2 ;
if ( t3 < 0 )
t3 = 0 - t3 ;
2013-05-09 10:44:08 -04:00
if ( ( t1 > iAggroRange )
| | ( t2 > iAggroRange )
| | ( t3 > iAggroRange )
| | ( mob - > IsInvisible ( this ) )
| | ( mob - > IsClient ( ) & &
( ! mob - > CastToClient ( ) - > Connected ( )
| | mob - > CastToClient ( ) - > IsLD ( )
| | mob - > CastToClient ( ) - > IsBecomeNPC ( )
| | mob - > CastToClient ( ) - > GetGM ( )
)
) )
2013-02-16 16:14:39 -08:00
{
return ( false ) ;
}
//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 ) ;
}
2013-05-09 10:44:08 -04:00
float dist2 = mob - > DistNoRoot ( * this ) ;
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 ;
if
(
//old InZone check taken care of above by !mob->CastToClient()->Connected()
(
2013-08-01 19:24:15 -05:00
( GetINT ( ) < = RuleI ( Aggro , IntAggroThreshold ) )
2013-02-16 16:14:39 -08:00
| | ( mob - > IsClient ( ) & & mob - > CastToClient ( ) - > IsSitting ( ) )
| | ( mob - > GetLevelCon ( GetLevel ( ) ) ! = CON_GREEN )
)
& &
(
(
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
& & MakeRandomInt ( 0 , 99 ) < THREATENLY_ARRGO_CHANCE - heroicCHA_mod
)
)
)
)
{
//FatherNiwtit: make sure we can see them. last since it is very expensive
if ( CheckLosFN ( mob ) ) {
// Aggro
# if EQDEBUG>=6
LogFile - > write ( EQEMuLog : : Debug , " Check aggro for %s target %s. " , GetName ( ) , mob - > GetName ( ) ) ;
# endif
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
}
2013-02-16 16:14:39 -08:00
# if EQDEBUG >= 6
2013-05-09 10:44:08 -04:00
printf ( " Is In zone?:%d \n " , mob - > InZone ( ) ) ;
printf ( " Dist^2: %f \n " , dist2 ) ;
printf ( " Range^2: %f \n " , iAggroRange2 ) ;
printf ( " Faction: %d \n " , fv ) ;
printf ( " Int: %d \n " , GetINT ( ) ) ;
printf ( " Con: %d \n " , GetLevelCon ( mob - > GetLevel ( ) ) ) ;
2013-05-06 13:07:41 -04:00
# endif
2013-02-16 16:14:39 -08:00
return ( false ) ;
}
Mob * EntityList : : AICheckCloseAggro ( Mob * sender , float iAggroRange , float iAssistRange ) {
if ( ! sender | | ! sender - > IsNPC ( ) )
2013-05-04 18:06:58 -07:00
return ( nullptr ) ;
2013-02-16 16:14:39 -08:00
# ifdef REVERSE_AGGRO
//with reverse aggro, npc->client is checked elsewhere, no need to check again
2014-02-10 10:39:12 -05:00
auto it = npc_list . begin ( ) ;
while ( it ! = npc_list . end ( ) ) {
2013-02-16 16:14:39 -08:00
# else
2014-02-10 10:39:12 -05:00
auto it = mob_list . begin ( ) ;
while ( it ! = mob_list . end ( ) ) {
2013-02-16 16:14:39 -08:00
# endif
2014-02-10 10:39:12 -05:00
Mob * mob = it - > second ;
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
if ( sender - > CheckWillAggro ( mob ) )
return mob ;
+ + it ;
2013-02-16 16:14:39 -08:00
}
//LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName());
2014-02-10 10:39:12 -05:00
return nullptr ;
2013-02-16 16:14:39 -08:00
}
2014-02-10 10:39:12 -05:00
int EntityList : : GetHatedCount ( Mob * attacker , Mob * exclude )
{
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 ;
if ( ! mob | | ( mob = = exclude ) )
continue ;
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
if ( ! mob - > IsEngaged ( ) )
continue ;
2013-02-16 16:14:39 -08:00
2014-02-10 10:39:12 -05:00
if ( mob - > IsFeared ( ) | | mob - > IsMezzed ( ) )
continue ;
2013-02-16 16:14:39 -08:00
2014-02-10 10:39:12 -05:00
if ( attacker - > GetLevelCon ( mob - > GetLevel ( ) ) = = CON_GREEN )
continue ;
2013-02-16 16:14:39 -08:00
2014-02-10 10:39:12 -05:00
if ( ! mob - > CheckAggro ( attacker ) )
continue ;
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
2014-02-10 10:39:12 -05:00
if ( mob - > DistNoRoot ( * attacker ) > AggroRange )
continue ;
2013-02-16 16:14:39 -08:00
Count + + ;
}
return Count ;
}
void EntityList : : AIYellForHelp ( Mob * sender , Mob * attacker ) {
if ( ! sender | | ! attacker )
return ;
if ( sender - > GetPrimaryFaction ( ) = = 0 )
return ; // well, if we dont have a faction set, we're gonna be indiff to everybody
2013-05-06 13:07:41 -04:00
2014-02-10 10:39:12 -05:00
for ( auto it = npc_list . begin ( ) ; it ! = npc_list . end ( ) ; + + it ) {
NPC * mob = it - > second ;
if ( ! mob )
2013-02-16 16:14:39 -08:00
continue ;
2014-02-10 10:39:12 -05:00
2013-02-16 16:14:39 -08:00
float r = mob - > GetAssistRange ( ) ;
r = r * r ;
if (
mob ! = sender
& & mob ! = attacker
// && !mob->IsCorpse()
// && mob->IsAIControlled()
& & mob - > GetPrimaryFaction ( ) ! = 0
& & mob - > DistNoRoot ( * sender ) < = r
& & ! mob - > IsEngaged ( )
& & ( ( ! mob - > IsPet ( ) ) | | ( mob - > IsPet ( ) & & mob - > GetOwner ( ) & & ! mob - > GetOwner ( ) - > IsClient ( ) ) )
// If we're a pet we don't react to any calls for help if our owner is a client
)
{
//if they are in range, make sure we are not green...
//then jump in if they are our friend
if ( attacker - > GetLevelCon ( mob - > GetLevel ( ) ) ! = CON_GREEN )
{
bool useprimfaction = false ;
if ( mob - > GetPrimaryFaction ( ) = = sender - > CastToNPC ( ) - > GetPrimaryFaction ( ) )
{
const NPCFactionList * cf = database . GetNPCFactionEntry ( mob - > GetNPCFactionID ( ) ) ;
if ( cf ) {
if ( cf - > assistprimaryfaction ! = 0 )
useprimfaction = true ;
}
}
if ( useprimfaction | | sender - > GetReverseFactionCon ( mob ) < = FACTION_AMIABLE )
{
//attacking someone on same faction, or a friend
2013-05-09 10:44:08 -04:00
//Father Nitwit: make sure we can see them.
2013-02-16 16:14:39 -08:00
if ( mob - > CheckLosFN ( sender ) ) {
2013-05-06 13:07:41 -04:00
# if (EQDEBUG>=5)
LogFile - > write ( EQEMuLog : : Debug , " AIYellForHelp( \" %s \" , \" %s \" ) %s attacking %s Dist %f Z %f " ,
2013-02-16 16:14:39 -08:00
sender - > GetName ( ) , attacker - > GetName ( ) , mob - > GetName ( ) , attacker - > GetName ( ) , mob - > DistNoRoot ( * sender ) , fabs ( sender - > GetZ ( ) + mob - > GetZ ( ) ) ) ;
# endif
mob - > AddToHateList ( attacker , 1 , 0 , false ) ;
}
}
}
}
}
}
/*
solar : returns false if attack should not be allowed
I try to list every type of conflict that ' s possible here , so it ' s easy
2013-05-09 10:44:08 -04:00
to see how the decision is made . Yea , it could be condensed and made
2013-02-16 16:14:39 -08:00
faster , but I ' m doing it this way to make it readable and easy to modify
*/
bool Mob : : IsAttackAllowed ( Mob * target , bool isSpellAttack )
{
Mob * mob1 , * mob2 , * tempmob ;
Client * c1 , * c2 , * becomenpc ;
// 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
2013-02-16 16:14:39 -08:00
// solar: the format here is a matrix of mob type vs mob type.
// 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
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 ) ;
LogFile - > write ( EQEMuLog : : Debug , " Mob::IsAttackAllowed: don't have a rule for this - %s vs %s \n " , this - > GetName ( ) , target - > GetName ( ) ) ;
return false ;
}
// solar: 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 )
{
Mob * mob1 , * mob2 , * tempmob ;
Client * c1 , * c2 ;
int reverse ;
if ( ! target )
return false ;
if ( target - > GetAllowBeneficial ( ) )
return true ;
// solar: 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 ) ;
LogFile - > write ( EQEMuLog : : Debug , " Mob::IsBeneficialAllowed: don't have a rule for this - %s to %s \n " , this - > GetName ( ) , target - > GetName ( ) ) ;
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
2014-08-23 03:21:36 -04:00
float _DistNoRoot = DistNoRoot ( * other ) ;
if ( GetSpecialAbility ( NPC_CHASE_DISTANCE ) ) {
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 ) ) ;
2014-08-24 19:11:41 -04:00
if ( GetSpecialAbilityParam ( NPC_CHASE_DISTANCE , 2 ) ) ;
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 ;
2014-08-24 19:11:41 -04:00
if ( ( DoLoSCheck & & CheckLastLosState ( ) ) & & ( _DistNoRoot > = min_dist & & _DistNoRoot < = max_dist ) )
2014-08-23 03:21:36 -04:00
SetPseudoRoot ( true ) ;
else
SetPseudoRoot ( false ) ;
}
if ( _DistNoRoot < = size_mod )
2013-02-16 16:14:39 -08:00
{
return true ;
}
return false ;
}
//Father Nitwit's LOS code
bool Mob : : CheckLosFN ( Mob * other ) {
bool Result = false ;
if ( other )
Result = CheckLosFN ( other - > GetX ( ) , other - > GetY ( ) , other - > GetZ ( ) , other - > GetSize ( ) ) ;
2014-08-23 03:21:36 -04:00
SetLastLosState ( Result ) ;
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
2014-02-23 21:17:28 -08:00
Map : : Vertex myloc ;
Map : : Vertex 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
LogFile - > write ( EQEMuLog : : Debug , " LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f) " , myloc . x , myloc . y , myloc . z , oloc . x , oloc . y , oloc . z , GetSize ( ) , mobSize ) ;
# endif
2014-02-23 21:17:28 -08:00
return zone - > zonemap - > CheckLoS ( myloc , oloc ) ;
2013-02-16 16:14:39 -08:00
}
//offensive spell aggro
2014-01-24 15:28:54 -05:00
int32 Mob : : CheckAggroAmount ( uint16 spell_id , bool isproc )
{
2013-02-16 16:14:39 -08:00
int32 AggroAmount = 0 ;
int32 nonModifiedAggro = 0 ;
uint16 slevel = GetLevel ( ) ;
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 )
AggroAmount + = ( 2 + ( ( slevel * slevel ) / 8 ) ) ;
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 )
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 5 ) ) ;
break ;
}
case SE_Stun : {
int val = ( 5 + ( ( slevel * slevel ) / 6 ) ) ;
if ( isproc & & RuleI ( Aggro , MaxStunProcAggro ) > - 1 & & ( val > RuleI ( Aggro , MaxStunProcAggro ) ) )
val = RuleI ( Aggro , MaxStunProcAggro ) ;
AggroAmount + = val ;
break ;
}
case SE_Blind : {
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 6 ) ) ;
break ;
}
case SE_Mez : {
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 5 ) ) ;
break ;
}
case SE_Charm : {
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 5 ) ) ;
break ;
}
case SE_Root : {
AggroAmount + = ( 2 + ( ( slevel * slevel ) / 8 ) ) ;
break ;
}
case SE_Fear : {
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 6 ) ) ;
break ;
}
case SE_ATK :
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 )
2014-01-24 15:28:54 -05:00
AggroAmount - = val * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
case SE_ResistMagic :
case SE_ResistFire :
case SE_ResistCold :
case SE_ResistPoison :
2014-01-24 15:28:54 -05:00
case SE_ResistDisease : {
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 * 3 ;
break ;
2013-02-16 16:14:39 -08:00
}
2014-01-24 15:28:54 -05:00
case SE_ResistAll : {
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 * 6 ;
break ;
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 )
2014-01-24 15:28:54 -05:00
AggroAmount - = val * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
2014-01-24 15:28:54 -05: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 - = val * 6 ;
break ;
2013-02-16 16:14:39 -08:00
}
2014-01-24 15:28:54 -05:00
case SE_BardAEDot : {
AggroAmount + = slevel * 2 ;
break ;
2013-02-16 16:14:39 -08:00
}
2014-01-24 15:28:54 -05:00
case SE_SpinTarget : {
AggroAmount + = ( 5 + ( ( slevel * slevel ) / 5 ) ) ;
break ;
2013-02-16 16:14:39 -08:00
}
case SE_Amnesia :
2014-01-24 15:28:54 -05:00
case SE_Silence : {
AggroAmount + = slevel * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
2014-01-24 15:28:54 -05:00
case SE_Destroy : {
AggroAmount + = slevel * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
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 ;
}
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 :
2014-01-24 15:28:54 -05:00
case SE_DispelDetrimental : {
2013-05-06 13:07:41 -04:00
AggroAmount + = slevel ;
2013-02-16 16:14:39 -08:00
break ;
}
case SE_ReduceHate :
2014-02-18 21:59:33 -05: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 ;
}
}
}
2014-01-24 15:28:54 -05:00
if ( IsAEDurationSpell ( spell_id ) )
2013-02-16 16:14:39 -08:00
AggroAmount / = 2 ;
2014-01-24 15:28:54 -05:00
if ( spells [ spell_id ] . HateAdded > 0 )
2013-02-16 16:14:39 -08:00
AggroAmount = spells [ spell_id ] . HateAdded ;
if ( IsBardSong ( spell_id ) )
AggroAmount = AggroAmount * RuleI ( Aggro , SongAggroMod ) / 100 ;
if ( GetOwner ( ) & & IsPet ( ) )
AggroAmount = AggroAmount * RuleI ( Aggro , PetSpellAggroMod ) / 100 ;
2013-05-06 13:07:41 -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-01-24 15:28:54 -05:00
if ( IsClient ( ) )
2013-02-16 16:14:39 -08:00
HateMod + = CastToClient ( ) - > GetFocusEffect ( focusSpellHateMod , spell_id ) ;
AggroAmount = ( AggroAmount * HateMod ) / 100 ;
//made up number probably scales a bit differently on live but it seems like it will be close enough
//every time you cast on live you get a certain amount of "this is a spell" aggro
//confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected
//by hate modifiers either.
2013-05-06 13:07:41 -04:00
//AggroAmount += (slevel*slevel/72);
// Saved so I can reimplement it;
2013-02-16 16:14:39 -08:00
// this should only be on the spell to aggro the npc not every spell
}
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
2014-01-24 15:28:54 -05:00
int32 Mob : : CheckHealAggroAmount ( uint16 spell_id , uint32 heal_possible )
{
2013-02-16 16:14:39 -08:00
int32 AggroAmount = 0 ;
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_CurrentHP : {
2014-01-17 22:09:41 -05:00
AggroAmount + = IsBuffSpell ( spell_id ) ? spells [ spell_id ] . mana / 4 : spells [ spell_id ] . mana ;
2013-02-16 16:14:39 -08:00
break ;
}
case SE_Rune : {
2014-01-24 15:28:54 -05:00
AggroAmount + = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ 0 ] , spells [ spell_id ] . base [ 0 ] , spells [ spell_id ] . max [ o ] , GetLevel ( ) , spell_id ) * 2 ;
2013-02-16 16:14:39 -08:00
break ;
}
2014-01-24 15:28:54 -05:00
case SE_HealOverTime : {
AggroAmount + = CalcSpellEffectValue_formula ( spells [ spell_id ] . formula [ o ] , spells [ spell_id ] . base [ o ] , spells [ spell_id ] . max [ o ] , GetLevel ( ) , spell_id ) ;
2013-02-16 16:14:39 -08:00
break ;
}
2014-01-24 15:28:54 -05:00
default : {
2013-05-09 10:44:08 -04:00
break ;
}
2013-02-16 16:14:39 -08:00
}
}
if ( IsBardSong ( spell_id ) )
AggroAmount = AggroAmount * RuleI ( Aggro , SongAggroMod ) / 100 ;
if ( GetOwner ( ) & & IsPet ( ) )
AggroAmount = AggroAmount * RuleI ( Aggro , PetSpellAggroMod ) / 100 ;
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-01-24 15:28:54 -05:00
if ( IsClient ( ) )
2013-02-16 16:14:39 -08:00
HateMod + = CastToClient ( ) - > GetFocusEffect ( focusSpellHateMod , spell_id ) ;
//Live AA - Spell casting subtlety
HateMod + = aabonuses . hatemod + spellbonuses . hatemod + itembonuses . hatemod ;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
AggroAmount = ( AggroAmount * HateMod ) / 100 ;
//made up number probably scales a bit differently on live but it seems like it will be close enough
//every time you cast on live you get a certain amount of "this is a spell" aggro
//confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected
//by hate modifiers either.
//AggroAmount += (slevel*slevel/72); // Moved Below
}
2014-01-24 15:28:54 -05:00
if ( AggroAmount < 0 )
2013-02-16 16:14:39 -08:00
return 0 ;
else
return AggroAmount ;
}
void Mob : : AddFeignMemory ( Client * attacker ) {
2013-05-04 18:06:58 -07:00
if ( feign_memory_list . empty ( ) & & AIfeignremember_timer ! = nullptr )
2013-02-16 16:14:39 -08:00
AIfeignremember_timer - > Start ( AIfeignremember_delay ) ;
feign_memory_list . insert ( attacker - > CharacterID ( ) ) ;
}
void Mob : : RemoveFromFeignMemory ( Client * attacker ) {
feign_memory_list . erase ( attacker - > CharacterID ( ) ) ;
2013-05-04 18:06:58 -07:00
if ( feign_memory_list . empty ( ) & & AIfeignremember_timer ! = nullptr )
2013-05-09 10:44:08 -04:00
AIfeignremember_timer - > Disable ( ) ;
2013-02-16 16:14:39 -08:00
if ( feign_memory_list . empty ( ) )
{
minLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMin ) ;
maxLastFightingDelayMoving = RuleI ( NPC , LastFightingDelayMovingMax ) ;
2013-05-04 18:06:58 -07:00
if ( AIfeignremember_timer ! = nullptr )
2013-02-16 16:14:39 -08:00
AIfeignremember_timer - > Disable ( ) ;
}
}
void Mob : : ClearFeignMemory ( ) {
2013-05-09 10:44:08 -04:00
std : : set < uint32 > : : iterator RememberedCharID = feign_memory_list . begin ( ) ;
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 ) ;
2013-05-04 18:06:58 -07:00
if ( AIfeignremember_timer ! = nullptr )
2013-02-16 16:14:39 -08:00
AIfeignremember_timer - > Disable ( ) ;
}
bool Mob : : PassCharismaCheck ( Mob * caster , Mob * spellTarget , uint16 spell_id ) {
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 ;
2013-02-16 16:14:39 -08:00
if ( IsCharmSpell ( spell_id ) ) {
2013-02-20 13:54:26 -08:00
if ( spells [ spell_id ] . powerful_flag = = - 1 ) //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-02-18 21:59:33 -05:00
if ( MakeRandomInt ( 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 ) )
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , 0 , 0 , true , true ) ;
else
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , 0 , 0 , false , true ) ;
2013-02-16 16:14:39 -08:00
//2: The mob makes a resistance check against the charm
2014-03-02 19:44:42 -05: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-02-18 21:59:33 -05:00
if ( MakeRandomInt ( 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.
resist_check = ResistSpell ( spells [ spell_id ] . resisttype , spell_id , caster , true ) ;
2014-03-02 19:44:42 -05:00
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 )
{
int amount = other - > GetHateAmount ( this ) - ( GetLevel ( ) * 13 ) ;
other - > SetHate ( this , std : : max ( 1 , amount ) ) ;
return ;
}