Fixed Merc spell recast timers, changed how Mercs add mobs to their hate lists (should prevent IsEngaged() issues), initial Caster DPS Merc spell casting AI, initial Merc stance implementation, Mercs now suspend when their owner dies to prevent them being bugged (until it can be fixed), added 2 seconds between spell casts to be live-like.

This commit is contained in:
badcaptain
2013-03-14 19:51:29 -04:00
parent af1f87a00f
commit 0a805feeec
11 changed files with 1104 additions and 130 deletions
+629 -120
View File
@@ -12,11 +12,12 @@
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "watermap.h"
#include "vld.h"
extern volatile bool ZoneLoaded;
Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
: NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000)
: NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000)
{
base_hp = d->max_hp;
base_mana = d->Mana;
@@ -52,7 +53,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
memset(equipment, 0, sizeof(equipment));
SetMercID(0);
SetStance(MercStancePassive);
SetStance(MercStanceBalanced);
rest_timer.Disable();
int r;
@@ -1564,32 +1565,8 @@ void Merc::AI_Process() {
return;
}
if(!IsEngaged()) {
if(GetFollowID()) {
Group* g = GetGroup();
if(g) {
if(MercOwner && MercOwner->GetTarget() && MercOwner->GetTarget()->IsNPC() && (MercOwner->GetTarget()->GetHateAmount(MercOwner) || MercOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(MercOwner->GetTarget())) {
float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius);
range = range * range;
if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) {
AddToHateList(MercOwner->GetTarget(), 1);
}
}
else {
for(int counter = 0; counter < g->GroupCount(); counter++) {
if(g->members[counter]) {
if(g->members[counter]->GetTarget() && g->members[counter]->GetTarget()->IsNPC() && g->members[counter]->GetTarget()->GetHateAmount(MercOwner) && IsAttackAllowed(MercOwner->GetTarget())) {
float range = g->HasRole(g->members[counter], RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius);
range = range * range;
if(g->members[counter]->GetTarget()->DistNoRootNoZ(*this) < range) {
AddToHateList(g->members[counter]->GetTarget(), 1);
}
}
}
}
}
}
}
if(check_target_timer.Check()) {
CheckHateList();
}
if(IsEngaged())
@@ -1620,9 +1597,11 @@ void Merc::AI_Process() {
if(GetHatedCount() < hateCount) {
SetHatedCount(hateCount);
if(!confidence_timer.Enabled()) {
confidence_timer.Start(10000);
}
if(!CheckConfidence()) {
if(!confidence_timer.Enabled()) {
confidence_timer.Start(10000);
}
}
}
//Check specific conditions for merc to lose confidence and flee (or regain confidence once fleeing)
@@ -1633,9 +1612,9 @@ void Merc::AI_Process() {
if(!CheckConfidence()) {
_lost_confidence = true;
//move to bottome of hate lists?
//move to bottom of hate lists?
//Iterate though hatelist
// SetHate(otherm hate, damage)
// SetHate(other, hate, damage)
if(RuleB(Combat, EnableFearPathing)) {
CalculateNewFearpoint();
@@ -1879,6 +1858,9 @@ void Merc::AI_Process() {
SetHatedCount(0);
confidence_timer.Disable();
_check_confidence = false;
if(!check_target_timer.Enabled())
check_target_timer.Start(2000, false);
if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) {
@@ -1987,14 +1969,20 @@ bool Merc::AI_EngagedCastCheck() {
}
break;
case MELEEDPS:
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) {
failedToCast = true;
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) {
failedToCast = true;
}
}
}
break;
case CASTERDPS:
failedToCast = true;
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
failedToCast = true;
}
}
break;
}
@@ -2158,7 +2146,8 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
extraMana = false;
}
else { //handle spell recast and recast timers
mercSpell.time_cancast = Timer::GetCurrentTime() + spells[spellid].recast_time;
SetSpellTimeCanCast(mercSpell.spellid, spells[spellid].recast_time);
//mercSpell.time_cancast = Timer::GetCurrentTime() + spells[spellid].recast_time;
if(spells[spellid].EndurTimerIndex > 0) {
SetSpellRecastTimer(spells[spellid].EndurTimerIndex, spellid, spells[spellid].recast_time);
@@ -2184,8 +2173,8 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
uint8 mercLevel = GetLevel();
bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once.
bool castedSpell = false;
bool isDiscipline = false;
if(HasGroup()) {
Group *g = GetGroup();
@@ -2199,6 +2188,17 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
selectedMercSpell.proc_chance = 0;
selectedMercSpell.time_cancast = 0;
switch(mercClass)
{
case TANK:
case MELEEDPS:
isDiscipline = true;
break;
default:
isDiscipline = false;
break;
}
switch (iSpellTypes) {
case SpellType_Heal: {
Mob* tar = NULL;
@@ -2301,8 +2301,7 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
}
if(selectedMercSpell.spellid > 0) {
if(AIDoSpellCast(selectedMercSpell.spellid, tar, -1))
castedSpell = true;
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1);
}
if(castedSpell) {
@@ -2362,8 +2361,8 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
if(!castedSpell && tar->GetPet()) {
//don't cast group spells on pets
if(IsGroupSpell(selectedMercSpell.spellid)
|| spells[selectedMercSpell.spellid].targettype == ST_Group
if(IsGroupSpell(selectedMercSpell.spellid)
|| spells[selectedMercSpell.spellid].targettype == ST_Group
|| spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) {
continue;
}
@@ -2374,8 +2373,9 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore();
if(AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime)) {
if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore())
if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) {
tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime);
}
castedSpell = true;
}
@@ -2387,7 +2387,6 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
break;
}
case SpellType_Nuke: {
bool isDiscipline = false;
switch(mercClass)
{
case TANK:
@@ -2409,29 +2408,43 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
selectedMercSpell = GetBestMercSpellForHate(this);
}
isDiscipline = true;
break;
case HEALER:
break;
case MELEEDPS:
isDiscipline = true;
break;
case CASTERDPS:
Mob* tar = GetTarget();
break;
selectedMercSpell = GetBestMercSpellForAENuke(this, tar);
if(selectedMercSpell.spellid == 0 && !tar->SpecAttacks[UNSTUNABLE] && !tar->IsStunned()) {
uint8 stunChance = 15;
if(MakeRandomInt(1, 100) <= stunChance) {
selectedMercSpell = GetBestMercSpellForStun(this);
}
}
if(selectedMercSpell.spellid == 0) {
uint8 lureChance = 25;
if(MakeRandomInt(1, 100) <= lureChance) {
selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar);
}
}
if(selectedMercSpell.spellid == 0) {
selectedMercSpell = GetBestMercSpellForNuke(this);
}
break;
}
if(selectedMercSpell.spellid > 0) {
if(isDiscipline) {
if(UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID())) {
castedSpell = true;
}
castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID());
}
else {
if(AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1)) {
castedSpell = true;
}
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1);
}
}
@@ -2505,6 +2518,30 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
break;
}
case SpellType_Escape: {
Mob* tar = GetTarget();
uint8 hpr = (uint8)GetHPRatio();
bool mayGetAggro = false;
if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) {
mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells
if (mayGetAggro) {
selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape);
if(selectedMercSpell.spellid == 0)
break;
if(isDiscipline) {
castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID());
}
else {
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1);
}
}
}
break;
}
}
}
}
@@ -2512,6 +2549,122 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
return castedSpell;
}
void Merc::CheckHateList() {
if(check_target_timer.Enabled())
check_target_timer.Disable();
if(!IsEngaged()) {
if(GetFollowID()) {
Group* g = GetGroup();
if(g) {
Mob* MercOwner = GetOwner();
if(MercOwner && MercOwner->GetTarget() && MercOwner->GetTarget()->IsNPC() && (MercOwner->GetTarget()->GetHateAmount(MercOwner) || MercOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(MercOwner->GetTarget())) {
float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius);
range = range * range;
if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) {
AddToHateList(MercOwner->GetTarget(), 1);
}
}
else {
std::list<NPC*> npc_list;
entity_list.GetNPCList(npc_list);
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) {
NPC* npc = *itr;
float dist = npc->DistNoRootNoZ(*this);
int radius = RuleI(Mercs, AggroRadius);
radius *= radius;
if(dist <= radius) {
for(int counter = 0; counter < g->GroupCount(); counter++) {
Mob* groupMember = g->members[counter];
if(groupMember) {
if(npc->IsOnHatelist(groupMember)) {
if(!hate_list.IsOnHateList(npc)) {
float range = g->HasRole(groupMember, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius);
range *= range;
if(npc->DistNoRootNoZ(*this) < range) {
hate_list.Add(npc, 1);
}
}
}
}
}
/*std::list<tHateEntry*> their_hate_list;
npc->GetHateList(their_hate_list);
std::list<tHateEntry*>::iterator hateEntryIter = their_hate_list.begin();
while(hateEntryIter != their_hate_list.end())
{
tHateEntry *entry = (*hateEntryIter);
if(g->IsGroupMember(entry->ent)) {
if(!hate_list.IsOnHateList(npc)) {
float range = g->HasRole(entry->ent, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius);
range *= range;
if(entry->ent->DistNoRootNoZ(*this) < range) {
hate_list.Add(entry->ent, 1);
}
}
}
hateEntryIter++;
}*/
}
}
}
}
}
}
}
bool Merc::HasOrMayGetAggro() {
bool mayGetAggro = false;
if(GetTarget() && GetTarget()->GetHateTop()) {
Mob *topHate = GetTarget()->GetHateTop();
if(topHate == this)
mayGetAggro = true; //I currently have aggro
else {
uint32 myHateAmt = GetTarget()->GetHateAmount(this);
uint32 topHateAmt = GetTarget()->GetHateAmount(topHate);
if(myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt/topHateAmt)*100) > 90) //I have 90% as much hate as top, next action may give me aggro
mayGetAggro = true;
}
}
return mayGetAggro;
}
bool Merc::CheckAENuke(Merc* caster, Mob* tar, uint16 spell_id, uint8 &numTargets) {
std::list<NPC*> npc_list;
entity_list.GetNPCList(npc_list);
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) {
NPC* npc = *itr;
if(npc->DistNoRootNoZ(*tar) <= spells[spell_id].aoerange * spells[spell_id].aoerange) {
if(!npc->IsMezzed()) {
numTargets++;
}
else {
numTargets = 0;
return false;
}
}
}
if(numTargets > 1)
return true;
return false;
}
int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) {
int16 realTotal = 0;
@@ -2937,6 +3090,7 @@ int8 Merc::GetChanceToCastBySpellType(int16 spellType) {
break;
}
case MELEEDPS:{
chance = 100;
break;
}
case CASTERDPS:{
@@ -3018,6 +3172,26 @@ int8 Merc::GetChanceToCastBySpellType(int16 spellType) {
case CASTERDPS:{
break;
}
}
break;
}
case SpellType_Escape: {
switch(mercClass)
{
case TANK: {
break;
}
case HEALER:{
break;
}
case MELEEDPS:{
chance = 100;
break;
}
case CASTERDPS:{
chance = 100;
break;
}
}
break;
}
@@ -3033,7 +3207,7 @@ bool Merc::CheckStance(int16 stance) {
if(stance == 0
|| (stance > 0 && stance == GetStance())
|| (stance < 0 && abs(stance) != GetStance())) {
|| (stance < 0 && abs(stance) == GetStance())) {
return true;
}
@@ -3158,7 +3332,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffect(Merc* caster, int spellEf
continue;
}
if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect)) {
if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect) && caster->CheckStance(mercSpellList[i].stance)) {
MercSpell MercSpell;
MercSpell.spellid = mercSpellList[i].spellid;
MercSpell.stance = mercSpellList[i].stance;
@@ -3188,7 +3362,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster
continue;
}
if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect)) {
if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect) && caster->CheckStance(mercSpellList[i].stance)) {
if(spells[mercSpellList[i].spellid].targettype == targetType) {
MercSpell MercSpell;
MercSpell.spellid = mercSpellList[i].spellid;
@@ -3282,30 +3456,25 @@ MercSpell Merc::GetBestMercSpellForHealOverTime(Merc* caster) {
result.time_cancast = 0;
if(caster) {
std::list<MercSpell> botHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime);
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
std::list<MercSpell> mercHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime);
for(std::list<MercSpell>::iterator mercSpellListItr = botHoTSpellList.begin(); mercSpellListItr != botHoTSpellList.end(); mercSpellListItr++) {
for(std::list<MercSpell>::iterator mercSpellListItr = mercHoTSpellList.begin(); mercSpellListItr != mercHoTSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsHealOverTimeSpell(mercSpellListItr->spellid)) {
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
// this is both to quit early to save cpu and to avoid casting bad spells
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
continue;
}
if (mercSpellListItr->spellid <= 0 || mercSpellListItr->spellid >= SPDAT_RECORDS) {
// this is both to quit early to save cpu and to avoid casting bad spells
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
continue;
}
if(mercSpellList[i].spellid == mercSpellListItr->spellid
&& (mercSpellList[i].type & SpellType_Heal)
&& CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellList[i].spellid;
result.stance = mercSpellList[i].stance;
result.type = mercSpellList[i].type;
result.slot = mercSpellList[i].slot;
result.proc_chance = mercSpellList[i].proc_chance;
result.time_cancast = mercSpellList[i].time_cancast;
}
if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
}
break;
@@ -3327,23 +3496,18 @@ MercSpell Merc::GetBestMercSpellForPercentageHeal(Merc* caster) {
result.time_cancast = 0;
if(caster && caster->AI_HasSpells()) {
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
std::list<MercSpell> mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP);
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
// this is both to quit early to save cpu and to avoid casting bad spells
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
continue;
}
if(IsCompleteHealSpell(mercSpellList[i].spellid)
&& CheckSpellRecastTimers(caster, mercSpellList[i].spellid)) {
result.spellid = mercSpellList[i].spellid;
result.stance = mercSpellList[i].stance;
result.type = mercSpellList[i].type;
result.slot = mercSpellList[i].slot;
result.proc_chance = mercSpellList[i].proc_chance;
result.time_cancast = mercSpellList[i].time_cancast;
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsCompleteHealSpell(mercSpellListItr->spellid)
&& CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
break;
}
@@ -3461,35 +3625,30 @@ MercSpell Merc::GetBestMercSpellForGroupHealOverTime(Merc* caster) {
result.time_cancast = 0;
if(caster) {
std::list<MercSpell> botHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime);
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
std::list<MercSpell> mercHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime);
for(std::list<MercSpell>::iterator mercSpellListItr = botHoTSpellList.begin(); mercSpellListItr != botHoTSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsGroupHealOverTimeSpell(mercSpellListItr->spellid)) {
for(std::list<MercSpell>::iterator mercSpellListItr = mercHoTSpellList.begin(); mercSpellListItr != mercHoTSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsGroupHealOverTimeSpell(mercSpellListItr->spellid)) {
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
// this is both to quit early to save cpu and to avoid casting bad spells
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
continue;
}
if(mercSpellList[i].spellid == mercSpellListItr->spellid
&& (mercSpellList[i].type & SpellType_Heal)
&& CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellList[i].spellid;
result.stance = mercSpellList[i].stance;
result.type = mercSpellList[i].type;
result.slot = mercSpellList[i].slot;
result.proc_chance = mercSpellList[i].proc_chance;
result.time_cancast = mercSpellList[i].time_cancast;
}
}
break;
if (mercSpellListItr->spellid <= 0 || mercSpellListItr->spellid >= SPDAT_RECORDS) {
// this is both to quit early to save cpu and to avoid casting bad spells
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
continue;
}
if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
}
break;
}
}
}
return result;
@@ -3744,6 +3903,339 @@ MercSpell Merc::GetBestMercSpellForCure(Merc* caster, Mob *tar) {
return result;
}
MercSpell Merc::GetBestMercSpellForStun(Merc* caster) {
MercSpell result;
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Stun);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
break;
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) {
MercSpell result;
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
if(caster) {
uint8 initialCastChance = 0;
uint8 castChanceFalloff = 75;
switch(caster->GetStance())
{
case MercStanceBurnAE:
initialCastChance = 50;
break;
case MercStanceBalanced:
initialCastChance = 25;
break;
case MercStanceBurn:
initialCastChance = 0;
break;
}
//check of we even want to cast an AE nuke
if(MakeRandomInt(1, 100) <= initialCastChance) {
result = GetBestMercSpellForAERainNuke(caster, tar);
//check if we have a spell & allow for other AE nuke types
if(result.spellid == 0 && MakeRandomInt(1, 100) <= castChanceFalloff) {
result = GetBestMercSpellForPBAENuke(caster, tar);
//check if we have a spell & allow for other AE nuke types
if(result.spellid == 0 && MakeRandomInt(1, 100) <= castChanceFalloff) {
result = GetBestMercSpellForTargetedAENuke(caster, tar);
}
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) {
MercSpell result;
int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.)
int numTargetsCheck = 1; //used to check for min number of targets to use AE
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
switch(caster->GetStance())
{
case MercStanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
numTargetsCheck = 2;
break;
}
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsAENukeSpell(mercSpellListItr->spellid) && !IsAERainNukeSpell(mercSpellListItr->spellid)
&& !IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
uint8 numTargets = 0;
if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) {
if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
}
}
break;
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) {
MercSpell result;
int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.)
int numTargetsCheck = 1; //used to check for min number of targets to use AE
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
switch(caster->GetStance())
{
case MercStanceBurnAE:
numTargetsCheck = 2;
break;
case MercStanceBalanced:
case MercStanceBurn:
numTargetsCheck = 3;
break;
}
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
uint8 numTargets = 0;
if(CheckAENuke(caster, caster, mercSpellListItr->spellid, numTargets)) {
if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
}
}
break;
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) {
MercSpell result;
int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.)
int numTargetsCheck = 1; //used to check for min number of targets to use AE
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
switch(caster->GetStance())
{
case MercStanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
numTargetsCheck = 2;
break;
}
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsAERainNukeSpell(mercSpellListItr->spellid) && MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
uint8 numTargets = 0;
if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) {
if(numTargets >= numTargetsCheck) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
}
}
break;
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) {
MercSpell result;
int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.)
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
if(caster) {
std::list<MercSpell> mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid)
&& MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
break;
}
}
}
return result;
}
MercSpell Merc::GetBestMercSpellForNukeByTargetResists(Merc* caster, Mob* target) {
MercSpell result;
bool spellSelected = false;
result.spellid = 0;
result.stance = 0;
result.type = 0;
result.slot = 0;
result.proc_chance = 0;
result.time_cancast = 0;
if(!target)
return result;
if(caster) {
const int lureResisValue = -100;
const int maxTargetResistValue = 300;
bool selectLureNuke = false;
if((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue))
selectLureNuke = true;
std::list<MercSpell> mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke);
for(std::list<MercSpell>::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); mercSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) {
if(selectLureNuke && (spells[mercSpellListItr->spellid].ResistDiff < lureResisValue)) {
spellSelected = true;
}
else {
if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_MAGIC)
&& (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue))
{
spellSelected = true;
}
else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_COLD)
&& (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue))
{
spellSelected = true;
}
else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_FIRE)
&& (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue))
{
spellSelected = true;
}
}
}
if(spellSelected) {
result.spellid = mercSpellListItr->spellid;
result.stance = mercSpellListItr->stance;
result.type = mercSpellListItr->type;
result.slot = mercSpellListItr->slot;
result.proc_chance = mercSpellListItr->proc_chance;
result.time_cancast = mercSpellListItr->time_cancast;
break;
}
}
}
return result;
}
bool Merc::GetNeedsCured(Mob *tar) {
bool needCured = false;
@@ -3919,6 +4411,14 @@ bool Merc::CheckDisciplineRecastTimers(Merc *caster, uint16 spell_id) {
return false;
}
void Merc::SetSpellTimeCanCast(uint16 spellid, uint32 recast_delay) {
for (int i = 0; i < merc_spells.size(); i++) {
if(merc_spells[i].spellid == spellid) {
merc_spells[i].time_cancast = Timer::GetCurrentTime() + recast_delay;
}
}
}
bool Merc::CheckTaunt() {
Mob* tar = GetTarget();
//Only taunt if we are not top on target's hate list
@@ -3941,8 +4441,11 @@ bool Merc::CheckAETaunt() {
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) {
NPC* npc = *itr;
float dist = npc->DistNoRootNoZ(*this);
int range = GetActSpellRange(mercSpell.spellid, spells[mercSpell.spellid].range);
range *= range;
if(npc->DistNoRootNoZ(*this) <= GetActSpellRange(mercSpell.spellid, spells[mercSpell.spellid].range)) {
if(dist <= range) {
if(!npc->IsMezzed()) {
if(HasGroup()) {
Group* g = GetGroup();
@@ -5058,6 +5561,7 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats)
merc->SetSuspended(false);
SetMerc(merc);
merc->Unsuspend(setMaxStats);
merc->SetStance(GetMercInfo().Stance);
}
}
@@ -5078,6 +5582,7 @@ bool Merc::Suspend() {
mercOwner->GetMercInfo().IsSuspended = true;
mercOwner->GetMercInfo().SuspendedTime = time(NULL) + RuleI(Mercs, SuspendIntervalS);
mercOwner->GetMercInfo().MercTimerRemaining = mercOwner->GetMercTimer().GetRemainingTime();
mercOwner->GetMercInfo().Stance = GetStance();
mercOwner->GetMercTimer().Disable();
//mercOwner->UpdateMercTimer();
mercOwner->SendMercSuspendResponsePacket(mercOwner->GetMercInfo().SuspendedTime);
@@ -5103,7 +5608,7 @@ bool Merc::Unsuspend(bool setMaxStats) {
uint32 suspendedTime = 0;
SetSuspended(false);
mercOwner->GetMercInfo().mercid = GetMercID();
mercOwner->GetMercInfo().IsSuspended = false;
mercOwner->GetMercInfo().SuspendedTime = 0;
@@ -5333,7 +5838,9 @@ void Client::SetMerc(Merc* newmerc) {
}
if (!newmerc) {
SetMercID(0);
GetMercInfo().mercid = 0;
GetMercInfo().MercTemplateID = 0;
GetMercInfo().myTemplate = NULL;
GetMercInfo().IsSuspended = false;
GetMercInfo().SuspendedTime = 0;
GetMercInfo().Gender = 0;
@@ -5354,6 +5861,8 @@ void Client::SetMerc(Merc* newmerc) {
GetMercInfo().IsSuspended = newmerc->IsSuspended();
GetMercInfo().SuspendedTime = 0;
GetMercInfo().Gender = newmerc->GetGender();
//GetMercInfo().State = newmerc->GetStance();
GetMercInfo().MercTimerRemaining = 0;
}
}