bad_captain
01-20-2011, 01:36 PM
Here is my official submission for bot updates fixes.. A full list of changes is in this thread http://www.eqemulator.org/forums/showthread.php?t=32867, but include SoD HP/Mana cals, rest regen, existing buffs working after spawning, and correct AC/ATK calcs. In addition to those, I have also fixed a couple of other things:
1- Hybrid bots were casting healing spells on mobs instead of nukes. This has been fixed.
2- Classes who can cast stun spells now will.
1746_optional_sql_bot_manaregen.sql
UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen';
mob.cpp
Index: mob.cpp
================================================== =================
--- mob.cpp (revision 1832)
+++ mob.cpp (working copy)
@@ -4182,7 +4182,9 @@
israidgrouped = false;
}
isgrouped = v;
- parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+ if(IsClient())
+ parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
}
void Mob::SetRaidGrouped(bool v)
@@ -4192,7 +4194,9 @@
isgrouped = false;
}
israidgrouped = v;
- parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+ if(IsClient())
+ parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
}
sint16 Mob::GetCriticalChanceBonus(int16 skill, bool aa_bonus)
bot.h
Index: bot.h
================================================== =================
--- bot.h (revision 1832)
+++ bot.h (working copy)
@@ -99,7 +99,8 @@
virtual void TryCriticalHit(Mob *defender, int16 skill, sint32 &damage);
virtual bool TryFinishingBlow(Mob *defender, SkillType skillinuse);
virtual void DoRiposte(Mob* defender);
- inline virtual sint16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); }
+ inline virtual sint16 GetATK();
+ uint16 GetPrimarySkillValue();
virtual void MeleeMitigation(Mob *attacker, sint32 &damage, sint32 minhit);
virtual void DoSpecialAttackDamage(Mob *who, SkillType skill, sint32 max_damage, sint32 min_damage = 1, sint32 hate_override = -1);
virtual void TryBackstab(Mob *other);
@@ -109,6 +110,7 @@
virtual bool TryHeadShot(Mob* defender, SkillType skillInUse);
virtual sint32 CheckAggroAmount(int16 spellid);
virtual void CalcBonuses();
+ void CalcItemBonuses();
virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
inline virtual bool IsPet() { return false; }
@@ -119,6 +121,7 @@
virtual sint32 CheckHealAggroAmount(int16 spellid, int32 heal_possible = 0);
virtual sint32 CalcMaxMana();
virtual void SetAttackTimer();
+ int32 GetClassHPFactor();
virtual sint32 CalcMaxHP();
bool DoFinishedSpellAETarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
@@ -137,6 +140,27 @@
bool IsStanding();
bool IsBotCasterCombatRange(Mob *target);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ;
+ inline virtual sint16 GetMaxStat();
+ inline virtual sint16 GetMaxResist();
+ inline virtual sint16 GetMaxSTR();
+ inline virtual sint16 GetMaxSTA();
+ inline virtual sint16 GetMaxDEX();
+ inline virtual sint16 GetMaxAGI();
+ inline virtual sint16 GetMaxINT();
+ inline virtual sint16 GetMaxWIS();
+ inline virtual sint16 GetMaxCHA();
+ inline virtual sint16 GetMaxMR();
+ inline virtual sint16 GetMaxPR();
+ inline virtual sint16 GetMaxDR();
+ inline virtual sint16 GetMaxCR();
+ inline virtual sint16 GetMaxFR();
+ inline virtual sint16 GetMaxCorrup();
+ sint32 CalcHPRegenCap();
+ sint32 CalcManaRegenCap();
+ sint32 LevelRegen();
+ sint32 CalcHPRegen();
+ sint32 CalcManaRegen();
+ void CalcRestState();
// AI Methods
virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
@@ -233,6 +257,7 @@
static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster);
static std::string GetBotMagicianPetType(Bot* botCaster);
static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType);
+ static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType);
static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target);
static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
@@ -263,6 +288,57 @@
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
BotRoleType GetBotRole() { return _botRole; }
bool IsBotCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+ bool IsBotINTCaster() { return (GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+ bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); }
+ inline virtual sint16 GetAC() { return AC; }
+ inline virtual sint16 GetSTR();
+ inline virtual sint16 GetSTA();
+ inline virtual sint16 GetDEX();
+ inline virtual sint16 GetAGI();
+ inline virtual sint16 GetINT();
+ inline virtual sint16 GetWIS();
+ inline virtual sint16 GetCHA();
+ inline virtual sint16 GetMR();
+ inline virtual sint16 GetFR();
+ inline virtual sint16 GetDR();
+ inline virtual sint16 GetPR();
+ inline virtual sint16 GetCR();
+ inline virtual sint16 GetCorrup();
+ //Heroic
+ inline virtual sint16 GetHeroicSTR() const { return itembonuses.HeroicSTR; }
+ inline virtual sint16 GetHeroicSTA() const { return itembonuses.HeroicSTA; }
+ inline virtual sint16 GetHeroicDEX() const { return itembonuses.HeroicDEX; }
+ inline virtual sint16 GetHeroicAGI() const { return itembonuses.HeroicAGI; }
+ inline virtual sint16 GetHeroicINT() const { return itembonuses.HeroicINT; }
+ inline virtual sint16 GetHeroicWIS() const { return itembonuses.HeroicWIS; }
+ inline virtual sint16 GetHeroicCHA() const { return itembonuses.HeroicCHA; }
+ inline virtual sint16 GetHeroicMR() const { return itembonuses.HeroicMR; }
+ inline virtual sint16 GetHeroicFR() const { return itembonuses.HeroicFR; }
+ inline virtual sint16 GetHeroicDR() const { return itembonuses.HeroicDR; }
+ inline virtual sint16 GetHeroicPR() const { return itembonuses.HeroicPR; }
+ inline virtual sint16 GetHeroicCR() const { return itembonuses.HeroicCR; }
+ inline virtual sint16 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; }
+ // Mod2
+ inline virtual sint16 GetShielding() const { return itembonuses.MeleeMitigation; }
+ inline virtual sint16 GetSpellShield() const { return itembonuses.SpellShield; }
+ inline virtual sint16 GetDoTShield() const { return itembonuses.DoTShielding; }
+ inline virtual sint16 GetStunResist() const { return itembonuses.StunResist; }
+ inline virtual sint16 GetStrikeThrough() const { return itembonuses.StrikeThrough; }
+ inline virtual sint16 GetAvoidance() const { return itembonuses.AvoidMeleeChance; }
+ inline virtual sint16 GetAccuracy() const { return itembonuses.HitChance; }
+ inline virtual sint16 GetCombatEffects() const { return itembonuses.ProcChance; }
+ inline virtual sint16 GetDS() const { return itembonuses.DamageShield; }
+ // Mod3
+ inline virtual sint16 GetHealAmt() const { return itembonuses.HealAmt; }
+ inline virtual sint16 GetSpellDmg() const { return itembonuses.SpellDmg; }
+ inline virtual sint16 GetClair() const { return itembonuses.Clairvoyance; }
+ inline virtual sint16 GetDSMit() const { return itembonuses.DSMitigation; }
+
+ inline virtual sint16 GetSingMod() const { return itembonuses.singingMod; }
+ inline virtual sint16 GetBrassMod() const { return itembonuses.brassMod; }
+ inline virtual sint16 GetPercMod() const { return itembonuses.percussionMod; }
+ inline virtual sint16 GetStringMod() const { return itembonuses.stringedMod; }
+ inline virtual sint16 GetWindMod() const { return itembonuses.windMod; }
// "SET" Class Methods
void SetBotSpellID(uint32 newSpellID);
@@ -315,6 +391,9 @@
int32 _lastZoneId;
bool _rangerAutoWeaponSelect;
BotRoleType _botRole;
+ unsigned int RestRegenHP;
+ unsigned int RestRegenMana;
+ Timer rest_timer;
// Private "base stats" Members
sint16 _baseMR;
@@ -336,12 +415,13 @@
int8 _baseGender; // Bots gender. Necessary to preserve the original value otherwise it can be changed by illusions.
// Class Methods
+ sint16 acmod();
void GenerateBaseStats();
void GenerateAppearance();
void GenerateArmorClass();
- void GenerateBaseHitPoints();
+ sint32 GenerateBaseHitPoints();
void GenerateAABonuses();
- void GenerateBaseManaPoints();
+ sint32 GenerateBaseManaPoints();
void GenerateSpecialAttacks();
void SetBotID(uint32 botID);
bool CalcBotHitChance(Mob* target, SkillType skillinuse, int Hand);
bot.cpp
Index: bot.cpp
================================================== =================
--- bot.cpp (revision 1832)
+++ bot.cpp (working copy)
@@ -5,7 +5,7 @@
#include "doors.h"
// This constructor is used during the bot create command
-Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false) {
+Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) {
if(botOwner) {
this->SetBotOwner(botOwner);
this->_botOwnerCharacterID = botOwner->CharacterID();
@@ -39,6 +39,8 @@
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
+ RestRegenHP = 0;
+ RestRegenMana = 0;
SetBotID(0);
SetBotSpellID(0);
@@ -48,6 +50,8 @@
SetPetChooser(false);
SetRangerAutoWeaponSelect(false);
+ rest_timer.Disable();
+
SetFollowDistance(184);
// Do this once and only in this constructor
@@ -57,13 +61,17 @@
GenerateArmorClass();
// Calculate HitPoints Last As It Uses Base Stats
- GenerateBaseHitPoints();
+ cur_hp = GenerateBaseHitPoints();
+ cur_mana = GenerateBaseManaPoints();
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
strcpy(this->name, this->GetCleanName());
}
// This constructor is used when the bot is loaded out of the database
-Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, int32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false) {
+Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, int32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) {
this->_botOwnerCharacterID = botOwnerCharacterID;
if(this->_botOwnerCharacterID > 0) {
@@ -94,6 +102,8 @@
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
+ RestRegenHP = 0;
+ RestRegenMana = 0;
SetBotID(botID);
SetBotSpellID(botSpellsID);
@@ -103,6 +113,8 @@
SetPetChooser(false);
SetRangerAutoWeaponSelect(false);
+ rest_timer.Disable();
+
SetFollowDistance(184);
strcpy(this->name, this->GetCleanName());
@@ -120,12 +132,17 @@
}
GenerateBaseStats();
- GenerateArmorClass();
- // Calculate HitPoints Last As It Uses Base Stats
- GenerateBaseHitPoints();
+ // Load saved buffs
+ LoadBuffs();
CalcBotStats(false);
+
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
+ cur_hp = max_hp;
+ cur_mana = max_mana;
}
Bot::~Bot() {
@@ -409,7 +426,7 @@
Wisdom += 15;
Charisma += 10;
Dexterity += 5;
- Attack =+ 17;
+ Attack += 17;
DiseaseResist += 8;
break;
case 4: // Ranger
@@ -763,65 +780,534 @@
}
+sint16 Bot::acmod() {
+ int agility = GetAGI();
+ int level = GetLevel();
+ if(agility < 1 || level < 1)
+ return(0);
+
+ if (agility <=74){
+ if (agility == 1)
+ return -24;
+ else if (agility <=3)
+ return -23;
+ else if (agility == 4)
+ return -22;
+ else if (agility <=6)
+ return -21;
+ else if (agility <=8)
+ return -20;
+ else if (agility == 9)
+ return -19;
+ else if (agility <=11)
+ return -18;
+ else if (agility == 12)
+ return -17;
+ else if (agility <=14)
+ return -16;
+ else if (agility <=16)
+ return -15;
+ else if (agility == 17)
+ return -14;
+ else if (agility <=19)
+ return -13;
+ else if (agility == 20)
+ return -12;
+ else if (agility <=22)
+ return -11;
+ else if (agility <=24)
+ return -10;
+ else if (agility == 25)
+ return -9;
+ else if (agility <=27)
+ return -8;
+ else if (agility == 28)
+ return -7;
+ else if (agility <=30)
+ return -6;
+ else if (agility <=32)
+ return -5;
+ else if (agility == 33)
+ return -4;
+ else if (agility <=35)
+ return -3;
+ else if (agility == 36)
+ return -2;
+ else if (agility <=38)
+ return -1;
+ else if (agility <=65)
+ return 0;
+ else if (agility <=70)
+ return 1;
+ else if (agility <=74)
+ return 5;
+ }
+ else if(agility <= 137) {
+ if (agility == 75){
+ if (level <= 6)
+ return 9;
+ else if (level <= 19)
+ return 23;
+ else if (level <= 39)
+ return 33;
+ else
+ return 39;
+ }
+ else if (agility >= 76 && agility <= 79){
+ if (level <= 6)
+ return 10;
+ else if (level <= 19)
+ return 23;
+ else if (level <= 39)
+ return 33;
+ else
+ return 40;
+ }
+ else if (agility == 80){
+ if (level <= 6)
+ return 11;
+ else if (level <= 19)
+ return 24;
+ else if (level <= 39)
+ return 34;
+ else
+ return 41;
+ }
+ else if (agility >= 81 && agility <= 85){
+ if (level <= 6)
+ return 12;
+ else if (level <= 19)
+ return 25;
+ else if (level <= 39)
+ return 35;
+ else
+ return 42;
+ }
+ else if (agility >= 86 && agility <= 90){
+ if (level <= 6)
+ return 12;
+ else if (level <= 19)
+ return 26;
+ else if (level <= 39)
+ return 36;
+ else
+ return 42;
+ }
+ else if (agility >= 91 && agility <= 95){
+ if (level <= 6)
+ return 13;
+ else if (level <= 19)
+ return 26;
+ else if (level <= 39)
+ return 36;
+ else
+ return 43;
+ }
+ else if (agility >= 96 && agility <= 99){
+ if (level <= 6)
+ return 14;
+ else if (level <= 19)
+ return 27;
+ else if (level <= 39)
+ return 37;
+ else
+ return 44;
+ }
+ else if (agility == 100 && level >= 7){
+ if (level <= 19)
+ return 28;
+ else if (level <= 39)
+ return 38;
+ else
+ return 45;
+ }
+ else if (level <= 6) {
+ return 15;
+ }
+ //level is >6
+ else if (agility >= 101 && agility <= 105){
+ if (level <= 19)
+ return 29;
+ else if (level <= 39)
+ return 39;// not verified
+ else
+ return 45;
+ }
+ else if (agility >= 106 && agility <= 110){
+ if (level <= 19)
+ return 29;
+ else if (level <= 39)
+ return 39;// not verified
+ else
+ return 46;
+ }
+ else if (agility >= 111 && agility <= 115){
+ if (level <= 19)
+ return 30;
+ else if (level <= 39)
+ return 40;// not verified
+ else
+ return 47;
+ }
+ else if (agility >= 116 && agility <= 119){
+ if (level <= 19)
+ return 31;
+ else if (level <= 39)
+ return 41;
+ else
+ return 47;
+ }
+ else if (level <= 19) {
+ return 32;
+ }
+ //level is > 19
+ else if (agility == 120){
+ if (level <= 39)
+ return 42;
+ else
+ return 48;
+ }
+ else if (agility <= 125){
+ if (level <= 39)
+ return 42;
+ else
+ return 49;
+ }
+ else if (agility <= 135){
+ if (level <= 39)
+ return 42;
+ else
+ return 50;
+ }
+ else {
+ if (level <= 39)
+ return 42;
+ else
+ return 51;
+ }
+ } else if(agility <= 300) {
+ if(level <= 6) {
+ if(agility <= 139)
+ return(21);
+ else if(agility == 140)
+ return(22);
+ else if(agility <= 145)
+ return(23);
+ else if(agility <= 150)
+ return(23);
+ else if(agility <= 155)
+ return(24);
+ else if(agility <= 159)
+ return(25);
+ else if(agility == 160)
+ return(26);
+ else if(agility <= 165)
+ return(26);
+ else if(agility <= 170)
+ return(27);
+ else if(agility <= 175)
+ return(28);
+ else if(agility <= 179)
+ return(28);
+ else if(agility == 180)
+ return(29);
+ else if(agility <= 185)
+ return(30);
+ else if(agility <= 190)
+ return(31);
+ else if(agility <= 195)
+ return(31);
+ else if(agility <= 199)
+ return(32);
+ else if(agility <= 219)
+ return(33);
+ else if(agility <= 239)
+ return(34);
+ else
+ return(35);
+ } else if(level <= 19) {
+ if(agility <= 139)
+ return(34);
+ else if(agility == 140)
+ return(35);
+ else if(agility <= 145)
+ return(36);
+ else if(agility <= 150)
+ return(37);
+ else if(agility <= 155)
+ return(37);
+ else if(agility <= 159)
+ return(38);
+ else if(agility == 160)
+ return(39);
+ else if(agility <= 165)
+ return(40);
+ else if(agility <= 170)
+ return(40);
+ else if(agility <= 175)
+ return(41);
+ else if(agility <= 179)
+ return(42);
+ else if(agility == 180)
+ return(43);
+ else if(agility <= 185)
+ return(43);
+ else if(agility <= 190)
+ return(44);
+ else if(agility <= 195)
+ return(45);
+ else if(agility <= 199)
+ return(45);
+ else if(agility <= 219)
+ return(46);
+ else if(agility <= 239)
+ return(47);
+ else
+ return(48);
+ } else if(level <= 39) {
+ if(agility <= 139)
+ return(44);
+ else if(agility == 140)
+ return(45);
+ else if(agility <= 145)
+ return(46);
+ else if(agility <= 150)
+ return(47);
+ else if(agility <= 155)
+ return(47);
+ else if(agility <= 159)
+ return(48);
+ else if(agility == 160)
+ return(49);
+ else if(agility <= 165)
+ return(50);
+ else if(agility <= 170)
+ return(50);
+ else if(agility <= 175)
+ return(51);
+ else if(agility <= 179)
+ return(52);
+ else if(agility == 180)
+ return(53);
+ else if(agility <= 185)
+ return(53);
+ else if(agility <= 190)
+ return(54);
+ else if(agility <= 195)
+ return(55);
+ else if(agility <= 199)
+ return(55);
+ else if(agility <= 219)
+ return(56);
+ else if(agility <= 239)
+ return(57);
+ else
+ return(58);
+ } else { //lvl >= 40
+ if(agility <= 139)
+ return(51);
+ else if(agility == 140)
+ return(52);
+ else if(agility <= 145)
+ return(53);
+ else if(agility <= 150)
+ return(53);
+ else if(agility <= 155)
+ return(54);
+ else if(agility <= 159)
+ return(55);
+ else if(agility == 160)
+ return(56);
+ else if(agility <= 165)
+ return(56);
+ else if(agility <= 170)
+ return(57);
+ else if(agility <= 175)
+ return(58);
+ else if(agility <= 179)
+ return(58);
+ else if(agility == 180)
+ return(59);
+ else if(agility <= 185)
+ return(60);
+ else if(agility <= 190)
+ return(61);
+ else if(agility <= 195)
+ return(61);
+ else if(agility <= 199)
+ return(62);
+ else if(agility <= 219)
+ return(63);
+ else if(agility <= 239)
+ return(64);
+ else
+ return(65);
+ }
+ }
+ else{
+ //seems about 21 agil per extra AC pt over 300...
+ return (65 + ((agility-300) / 21));
+ }
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Error, "Error in Bot::acmod(): Agility: %i, Level: %i",agility,level);
+#endif
+ return 0;
+}
+
void Bot::GenerateArmorClass() {
- // Base AC
- int bac = GetAC();
- switch(this->GetClass()) {
- case WARRIOR:
- case SHADOWKNIGHT:
- case PALADIN:
- bac = bac*1.5;
+ /// new formula
+ int avoidance = 0;
+ avoidance = (acmod() + ((GetSkill(DEFENSE)*16)/9));
+ if (avoidance < 0)
+ avoidance = 0;
+
+ int mitigation = 0;
+ if (GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) {
+ mitigation = GetSkill(DEFENSE)/4 + (itembonuses.AC+1);
+ mitigation -= 4;
+ } else {
+ mitigation = GetSkill(DEFENSE)/3 + ((itembonuses.AC*4)/3);
+ if(GetClass() == MONK)
+ mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close...
}
+ int displayed = 0;
+ displayed += ((avoidance+mitigation)*1000)/847; //natural AC
+
+ //Iksar AC, untested
+ if (GetRace() == IKSAR) {
+ displayed += 12;
+ int iksarlevel = GetLevel();
+ iksarlevel -= 10;
+ if (iksarlevel > 25)
+ iksarlevel = 25;
+ if (iksarlevel > 0)
+ displayed += iksarlevel * 12 / 10;
+ }
+
+ //spell AC bonuses are added directly to natural total
+ displayed += spellbonuses.AC;
- this->AC = bac;
+ this->AC = displayed;
}
-void Bot::GenerateBaseHitPoints() {
- // Calc Base Hit Points
- /*int16 multiplier = 1;
- switch(this->GetClass()) {
- case WARRIOR:
- multiplier = 220;
+uint16 Bot::GetPrimarySkillValue()
+{
+ SkillType skill = HIGHEST_SKILL; //because NULL == 0, which is 1H Slashing, & we want it to return 0 from GetSkill
+ bool equiped = m_inv.GetItem(SLOT_PRIMARY);
+
+ if (!equiped)
+ skill = HAND_TO_HAND;
+ else {
+
+ uint8 type = m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType; //is this the best way to do this?
+
+ switch (type)
+ {
+ case ItemType1HS: // 1H Slashing
+ {
+ skill = _1H_SLASHING;
break;
- case DRUID:
- case CLERIC:
- case SHAMAN:
- multiplier = 150;
+ }
+ case ItemType2HS: // 2H Slashing
+ {
+ skill = _2H_SLASHING;
break;
- case BERSERKER:
- case PALADIN:
- case SHADOWKNIGHT:
- multiplier = 210;
+ }
+ case ItemTypePierce: // Piercing
+ {
+ skill = PIERCING;
break;
- case MONK:
- case BARD:
- case ROGUE:
- case BEASTLORD:
- multiplier = 180;
+ }
+ case ItemType1HB: // 1H Blunt
+ {
+ skill = _1H_BLUNT;
break;
- case RANGER:
- multiplier = 200;
+ }
+ case ItemType2HB: // 2H Blunt
+ {
+ skill = _2H_BLUNT;
break;
- case MAGICIAN:
- case WIZARD:
- case NECROMANCER:
- case ENCHANTER:
- multiplier = 120;
+ }
+ case ItemType2HPierce: // 2H Piercing
+ {
+ skill = PIERCING;
break;
+ }
+ case ItemTypeHand2Hand: // Hand to Hand
+ {
+ skill = HAND_TO_HAND;
+ break;
+ }
+ default: // All other types default to Hand to Hand
+ {
+ skill = HAND_TO_HAND;
+ break;
+ }
+ }
}
- int16 lm = multiplier;*/
+
+ return GetSkill(skill);
+}
+
+sint16 Bot::GetATK() {
+ int16 AttackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ if(WornCap > (RuleI(Character, ItemATKCap) + spellbonuses.ATK)) // GetATKBonus returns item and spell bonuses, so to keep under cap need to take into account
+ WornCap = (RuleI(Character, ItemATKCap) + spellbonuses.ATK);
+
+ AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69));
+
+ if (AttackRating < 10)
+ AttackRating = 10;
+
+ return AttackRating;
+}
+
+sint32 Bot::GenerateBaseHitPoints() {
+ // Calc Base Hit Points
+ int new_base_hp = 0;
int16 lm = GetClassLevelFactor();
int16 Post255;
+ int16 NormalSTA = GetSTA();
- if((this->GetSTA()-255)/2 > 0)
- Post255 = (this->GetSTA()-255)/2;
- else
- Post255 = 0;
+ if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ float SoDPost255;
+
+ if(((NormalSTA - 255) / 2) > 0)
+ SoDPost255 = ((NormalSTA - 255) / 2);
+ else
+ SoDPost255 = 0;
- int new_base_hp = (5)+(GetLevel()*lm/10) + (((this->GetSTA()-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000);
+ int hp_factor = GetClassHPFactor();
+
+ if (level < 41) {
+ new_base_hp = (5 + (GetLevel() * hp_factor / 12) +
+ ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600));
+ }
+ else if (level < 81) {
+ new_base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800));
+ }
+ else {
+ new_base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * hp_factor / 45));
+ }
+ }
+ else{
+ if((NormalSTA-255)/2 > 0)
+ Post255 = (NormalSTA-255)/2;
+ else
+ Post255 = 0;
+ new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000);
+ }
+
this->base_hp = new_base_hp;
- this->cur_hp = this->base_hp;
+
+ return new_base_hp;
}
void Bot::GenerateAABonuses() {
@@ -830,6 +1316,8 @@
if(botlevel >= 51) {
//level 51 = 1 AA level
uint8 botAAlevels = botlevel - 50;
+ if(botAAlevels > 15)
+ botAAlevels = 15;
STR += botAAlevels * 2; // Innate Strength AAs
STA += botAAlevels * 2; // Innate Stamina AAs
AGI += botAAlevels * 2; // Innate Agility AAs
@@ -1252,6 +1740,7 @@
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
buffs[BuffCount].casterAARank = atoi(DataRow[12]);
+ buffs[BuffCount].casterid = 0;
bool IsPersistent = false;
@@ -1658,48 +2147,28 @@
//6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting
if(!IsMoving() && !IsEngaged()) {
SendPosition();
+
+ if(IsSitting()){
+ if(!rest_timer.Enabled()){
+ rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
+ }
+ }
}
SpellProcess();
BuffProcess();
+ CalcRestState();
+
if(curfp)
ProcessFlee();
- int32 bonus = 0;
-
- // Med is meditating
- if(IsSitting())
- bonus += 3;
-
- //sint32 OOCRegen = 0;
- //if(oocregen > 0){ //should pull from Mob class
- // OOCRegen += GetMaxHP() * oocregen / 100;
- //}
-
- //Lieka Edit: Fixing NPC regen. NPCs should regen to full during a set duration, not based on their HPs. Increase NPC's HPs by % of total HPs / tick.
- //if((GetHP() < GetMaxHP()) && !IsPet()) {
- // if(!IsEngaged()) {//NPC out of combat
- // if(hp_regen > OOCRegen)
- // SetHP(GetHP() + hp_regen);
- // else
- // SetHP(GetHP() + OOCRegen);
- // } else
- // SetHP(GetHP()+hp_regen);
- //} else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) {
- // if(!IsEngaged()) //pet
- // SetHP(GetHP()+hp_regen+bonus+(GetLevel()/5));
- // else
- // SetHP(GetHP()+hp_regen+bonus);
- //} else
- // SetHP(GetHP()+hp_regen);
-
if(GetHP() < GetMaxHP())
- SetHP(GetHP() + hp_regen + bonus);
+ SetHP(GetHP() + CalcHPRegen() + RestRegenHP);
if(GetMana() < GetMaxMana())
- SetMana(GetMana() + mana_regen + bonus);
+ SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
}
if (sendhpupdate_timer.Check()) {
@@ -1731,128 +2200,27 @@
if(isSitting) {
// If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate
if(GetManaRatio() < 99.0f) {
- if(mana_timer.Check(true)) {
- SetAppearance(eaSitting, false);
- /*if(!((int)GetManaRatio() % 24)) {
- Say("Medding for Mana. I have %3.1f%% of %d mana. It is: %d", GetManaRatio(), GetMaxMana(), GetMana());
- }*/
- int32 level = GetLevel();
- int32 regen = (((GetSkill(MEDITATE)/10)+(level-(level/4)))/4)+4;
- spellbonuses.ManaRegen = 0;
- for(int j=0; j<BUFF_COUNT; j++) {
- if(buffs[j].spellid != 65535) {
- const SPDat_Spell_Struct &spell = spells[buffs[j].spellid];
- for(int i=0; i<EFFECT_COUNT; i++) {
- if(IsBlankSpellEffect(buffs[j].spellid, i))
- continue;
- int effect = spell.effectid[i];
- switch(effect) {
- case SE_CurrentMana:
- spellbonuses.ManaRegen += CalcSpellEffectValue(buffs[j].spellid, i, buffs[j].casterlevel);
- break;
- }
- }
- }
- }
- regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen);
- if(level >= 55) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 56) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 57) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 71) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 72) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 73) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 74) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 75) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
-
- float mana_regen_rate = RuleR(Bots, BotManaRegen);
- if(mana_regen_rate < 1.0f)
- mana_regen_rate = 1.0f;
-
- regen = regen / mana_regen_rate;
-
- SetMana(GetMana() + regen);
- }
+ if(!IsSitting())
+ Sit();
}
else {
- SetAppearance(eaStanding, false);
+ if(IsSitting())
+ Stand();
}
}
else {
- // Let's check our mana in fights..
- if(mana_timer.Check(true)) {
- /*if((!((int)GetManaRatio() % 12)) && ((int)GetManaRatio() < 10)) {
- Say("Medding for Mana. I have %3.1f%% of %d mana. It is: %d", GetManaRatio(), GetMaxMana(), GetMana());
- }*/
- int32 level = GetLevel();
- spellbonuses.ManaRegen = 0;
- for(int j=0; j<BUFF_COUNT; j++) {
- if(buffs[j].spellid != 65535) {
- const SPDat_Spell_Struct &spell = spells[buffs[j].spellid];
- for(int i=0; i<EFFECT_COUNT; i++) {
- if(IsBlankSpellEffect(buffs[j].spellid, i))
- continue;
- int effect = spell.effectid[i];
- switch(effect) {
- case SE_CurrentMana:
- spellbonuses.ManaRegen += CalcSpellEffectValue(buffs[j].spellid, i, buffs[j].casterlevel);
- break;
- }
- }
- }
- }
- int32 regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen + (level/5);
- if(level >= 55) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 56) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 57) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 71) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 72) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 73) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 74) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 75) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+ if(IsSitting())
+ Stand();
+ }
- float mana_regen_rate = RuleR(Bots, BotManaRegen);
- if(mana_regen_rate < 1.0f)
- mana_regen_rate = 1.0f;
-
- regen = regen / mana_regen_rate;
-
- SetMana(GetMana() + regen);
+ if(IsSitting()){
+ if(!rest_timer.Enabled()){
+ rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
}
}
+ else {
+ rest_timer.Disable();
+ }
}
bool Bot::BotRangedAttack(Mob* other) {
@@ -2175,6 +2543,9 @@
if(IsEngaged()) {
_ZP(Mob_BOT_Process_IsEngaged);
+ if(rest_timer.Enabled())
+ rest_timer.Disable();
+
if(IsRooted())
SetTarget(hate_list.GetClosest(this));
else
@@ -2533,6 +2904,8 @@
if(dist > GetFollowDistance()) {
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
+ if(rest_timer.Enabled())
+ rest_timer.Disable();
return;
}
else {
@@ -2842,9 +3215,6 @@
else
this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());
- // Load saved buffs
- LoadBuffs();
-
// Spawn the bot at the bow owner's loc
this->x_pos = botCharacterOwner->GetX();
this->y_pos = botCharacterOwner->GetY();
@@ -4855,7 +5225,7 @@
Group *g = GetGroup();
if(g) {
for(int i=0; i<MAX_GROUP_MEMBERS; i++) {
- if(g->members[i] && g->members[i]->IsBot() && !g->members[i]->CheckAggro(from)) {
+ if(g->members[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from)) {
g->members[i]->AddToHateList(from, 1);
}
}
@@ -5673,11 +6043,8 @@
bBlockFromRear = true;
if (damage > 0 && CanThisClassBlock() && (!other->BehindMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) {
- //skill = CastToClient()->GetSkill(BLOCKSKILL);
- /*if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(BLOCKSKILL, other, -10);
- }*/
-
+ skill = GetSkill(BLOCKSKILL);
+
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/35.0 + (GetDEX()/200);
RollTable[1] = RollTable[0] + bonus;
@@ -5687,8 +6054,8 @@
RollTable[1] = RollTable[0];
}
- if(damage > 0 && GetAA(aaShieldBlock) && (!other->BehindMob(this, other->GetX(), other->GetY()))) {
- /*bool equiped = CastToClient()->m_inv.GetItem(14);
+/* if(damage > 0 && GetAA(aaShieldBlock) && (!other->BehindMob(this, other->GetX(), other->GetY()))) {
+ bool equiped = CastToClient()->m_inv.GetItem(14);
if(equiped) {
uint8 shield = CastToClient()->m_inv.GetItem(14)->GetItem()->ItemType;
@@ -5705,19 +6072,16 @@
break;
}
}
- }*/
- }
+ }
+ }*/
//////////////////////////////////////////////////////
// parry
//////////////////////////////////////////////////////
if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY()))
{
- /*skill = CastToClient()->GetSkill(PARRY);
- if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(PARRY, other, -10);
- }*/
-
+ skill = GetSkill(PARRY);
+
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetDEX()/200);
bonus = bonus * (100 + defender->GetSpellBonuses().ParryChance + defender->GetItemBonuses().ParryChance) / 100.0f;
@@ -5734,10 +6098,7 @@
if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY()))
{
- /*skill = CastToClient()->GetSkill(DODGE);
- if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(DODGE, other, -10);
- }*/
+ skill = GetSkill(DODGE);
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetAGI()/200);
@@ -5851,36 +6212,6 @@
critChance += 2;
}
- switch(GetAA(aaCombatFury))
- {
- case 1:
- critChance += 2;
- break;
- case 2:
- critChance += 4;
- break;
- case 3:
- critChance += 7;
- break;
- default:
- break;
- }
-
- switch(GetAA(aaFuryoftheAges))
- {
- case 1:
- critChance += 1;
- break;
- case 2:
- critChance += 3;
- break;
- case 3:
- critChance += 5;
- break;
- default:
- break;
- }
-
int CritBonus = GetCriticalChanceBonus(skill);
if(CritBonus > 0) {
if(critChance == 0) //If we have a bonus to crit in items or spells but no actual chance to crit
@@ -5888,25 +6219,9 @@
else
critChance += (critChance * CritBonus / 100); //crit chance is a % increase to your reg chance
}
- if(GetAA(aaSlayUndead)){
- if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){
- switch(GetAA(aaSlayUndead)){
- case 1:
- critMod += 33;
- break;
- case 2:
- critMod += 66;
- break;
- case 3:
- critMod += 100;
- break;
- }
- slayUndeadCrit = true;
- }
- }
// Paladin Bot Slay Undead AA
- if(GetClass() == PALADIN) {
+ if(GetClass() == PALADIN && GetLevel() >= 59) {
if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) {
if(GetLevel() >= 61) {
critMod += 100;
@@ -5917,6 +6232,7 @@
else if(GetLevel() >= 59) {
critMod += 33;
}
+ slayUndeadCrit = true;
}
}
@@ -5952,35 +6268,43 @@
else {
entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s scores a critical hit!(%d)", GetCleanName(), damage);
}
- /*else {
- entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s scores a critical hit!(%d)", GetCleanName(), damage);
- }*/
}
}
}
bool Bot::TryFinishingBlow(Mob *defender, SkillType skillinuse)
{
- int8 aa_item = GetAA(aaFinishingBlow) + GetAA(aaCoupdeGrace) + GetAA(aaDeathblow);
-
- if(GetLevel() >= 55) {
- aa_item += 1; // Finishing Blow AA 1
+ int8 aa_item = 0;
+ // Deathblow
+ if(GetLevel() >= 68) {
+ aa_item = 9;
}
- if(GetLevel() >= 56) {
- aa_item += 1; // Finishing Blow AA 2
+ else if(GetLevel() >= 67) {
+ aa_item = 8;
}
- if(GetLevel() >= 57) {
- aa_item += 1; // Finishing Blow AA 3
+ else if(GetLevel() >= 66) {
+ aa_item = 7;
}
- if(GetLevel() >= 62) {
- aa_item += 1; // Coup de Grace AA 1
+ // Coup de Grace AA
+ else if(GetLevel() >= 64) {
+ aa_item = 6;
}
- if(GetLevel() >= 63) {
- aa_item += 1; // Coup de Grace AA 2
+ else if(GetLevel() >= 63) {
+ aa_item = 5;
}
- if(GetLevel() >= 64) {
- aa_item += 1; // Coup de Grace AA 3
+ else if(GetLevel() >= 62) {
+ aa_item = 4;
}
+ // Finishing Blow AA
+ else if(GetLevel() >= 57) {
+ aa_item = 3;
+ }
+ else if(GetLevel() >= 56) {
+ aa_item = 2;
+ }
+ else if(GetLevel() >= 55) {
+ aa_item = 1;
+ }
if(aa_item && defender->GetHPRatio() < 10){
int chance = 0;
@@ -6155,22 +6479,6 @@
Mob* defender = this;
int totalMit = 0;
- switch(GetAA(aaCombatStability)){
- case 1:
- totalMit += 2;
- break;
- case 2:
- totalMit += 5;
- break;
- case 3:
- totalMit += 10;
- break;
- }
-
- totalMit += GetAA(aaPhysicalEnhancement)*2;
- totalMit += GetAA(aaInnateDefense);
- totalMit += GetAA(aaDefensiveInstincts)*0.5;
-
int8 botclass = GetClass();
uint8 botlevel = GetLevel();
@@ -6189,17 +6497,9 @@
}
// All Melee get Physical Enhancement AA
- if((botclass != WIZARD) &&
- (botclass != NECROMANCER) &&
- (botclass != MAGICIAN) &&
- (botclass != ENCHANTER) &&
- (botclass != DRUID) &&
- (botclass != SHAMAN))
+ if(!IsBotCaster() && botlevel >= 59)
{
- if(botlevel >= 59)
- { // Physical Enhancement AA
- totalMit += 2;
- }
+ totalMit += 2;
}
// Everyone gets Innate Defense AA
@@ -6225,9 +6525,8 @@
}
// All but pure casters get Defensive Instincts AA
- if((botclass != WIZARD) && (botclass != NECROMANCER) && (botclass != MAGICIAN) && (botclass != ENCHANTER))
+ if(!IsBotINTCaster())
{
- // Clients get this AA multiplied by a float to equal an int(totalMit)? Unfair rounding
if(botlevel >= 70) { // Defensive Instincts AA 5
totalMit += 5;
}
@@ -6438,7 +6737,7 @@
else if(GetLevel() == 65) { // Triple Backstab AA 1
tripleChance = 10;
}
- if (tripleChance > MakeRandomInt(1, 100)) {
+ if (tripleChance > MakeRandomInt(0, 99)) {
tripleBackstab = true;
}
@@ -6483,19 +6782,19 @@
}
}
}
- else if(GetAA(aaChaoticStab) > 0) {
+ // Chaotic Stab
+ else if(level >= 59) {
//we can stab from any angle, we do min damage though.
RogueBackstab(other, true);
- if (level > 54) {
- float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
- // Check for double attack with main hand assuming maxed DA Skill (MS)
- if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA
- if(other->GetHP() > 0)
- RogueBackstab(other, true);
+ float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
+ // Check for double attack with main hand assuming maxed DA Skill (MS)
+ if(MakeRandomFloat(0, 1) < DoubleAttackProbability) { // Max 62.4 % chance of DA
+ if(other->GetHP() > 0)
+ RogueBackstab(other, true);
if (tripleBackstab && other->GetHP() > 0) {
- RogueBackstab(other);
- }
+ RogueBackstab(other);
+ }
}
}
else { //We do a single regular attack if we attack from the front without chaotic stab
@@ -6665,7 +6964,7 @@
Taunt(target->CastToNPC(), true);
}*/
Say("Taunting %s", target->GetCleanName());
- Taunt(target->CastToNPC(), true);
+ Taunt(target->CastToNPC(), false);
}
if(!ca_time)
@@ -6970,6 +7269,22 @@
hasRuleDefined = true;
Result = true;
}
+ else if(attacker->IsClient() && target->IsBot()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
+ else if(attacker->IsBot() && target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = true;
+ }
+ else if(attacker->IsBot() && !target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
+ else if(attacker->IsPet() && attacker->IsFamiliar()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
else if(attacker->IsBot() && attacker->CastToBot()->GetBotOwner() && attacker->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) {
if(target->IsBot() && target->GetOwner() && target->GetOwner()->CastToClient()->GetPVP()) {
// my target is a bot and it's owner is pvp
@@ -6995,23 +7310,15 @@
Result = true;
}
}
+ else if(target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = true;
+ }
+ else if(!target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
}
- else if(attacker->IsClient() && target->IsBot()) {
- hasRuleDefined = true;
- Result = false;
- }
- else if(attacker->IsBot() && target->IsNPC()) {
- hasRuleDefined = true;
- Result = true;
- }
- else if(attacker->IsBot() && !target->IsNPC()) {
- hasRuleDefined = true;
- Result = false;
- }
- else if(attacker->IsPet() && attacker->IsFamiliar()) {
- hasRuleDefined = true;
- Result = false;
- }
}
return Result;
@@ -7199,49 +7506,29 @@
}
sint32 Bot::CalcMaxMana() {
- sint32 WisInt = 0;
- sint32 MindLesserFactor, MindFactor;
- switch (GetCasterClass()) {
- case 'I':
- WisInt = GetINT();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
- }
- else {
- MindLesserFactor = 0;
- }
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- max_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- max_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
- max_mana += (itembonuses.Mana + spellbonuses.Mana);
+ switch(GetCasterClass())
+ {
+ case 'I':
+ case 'W': {
+ max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana);
break;
-
- case 'W':
- WisInt = GetWIS();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
- }
- else {
- MindLesserFactor = 0;
- }
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- max_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- max_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
- max_mana += (itembonuses.Mana + spellbonuses.Mana);
+ }
+ case 'N': {
+ max_mana = 0;
break;
-
- default:
+ }
+ default: {
+ LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass());
max_mana = 0;
break;
+ }
}
+ if (cur_mana > max_mana) {
+ cur_mana = max_mana;
+ }
+ else if (max_mana < 0) {
+ max_mana = 0;
+ }
return max_mana;
}
@@ -7703,7 +7990,7 @@
}
if(IsDamageSpell(spell_id) && spells[spell_id].cast_time >= 4000) {
- if((botclass == DRUID)||(botclass == WIZARD)) {
+ if((botclass == DRUID)||(botclass == WIZARD)||(botclass == MAGICIAN)) {
if(botlevel >= 61) { // Quick Damage AA
cast_reducer += 10;
}
@@ -7850,7 +8137,6 @@
}
void Bot::DoBuffTic(int16 spell_id, int32 ticsremaining, int8 caster_level, Mob* caster) {
- if(caster && !caster->IsCorpse())
Mob::DoBuffTic(spell_id, ticsremaining, caster_level, caster);
}
@@ -8100,45 +8386,102 @@
return Result;
}
-void Bot::GenerateBaseManaPoints() {
+sint32 Bot::GenerateBaseManaPoints() {
// Now, we need to calc the base mana.
sint32 bot_mana = 0;
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+
switch (GetCasterClass()) {
case 'I':
WisInt = INT;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ if (WisInt > 100) {
+ ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
+ if (WisInt > 201) {
+ ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
+ }
+ }
+ else {
+ ConvertedWisInt = WisInt;
+ }
+ if (GetLevel() < 41) {
+ wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
+ base_mana = (GetLevel() * 15);
+ }
+ else if (GetLevel() < 81) {
+ wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
+ base_mana = (600 + ((GetLevel() - 40) * 30));
+ }
+ else {
+ wisint_mana = (9 * ConvertedWisInt);
+ base_mana = (1800 + ((GetLevel() - 80) * 18));
+ }
+ bot_mana = base_mana + wisint_mana;
}
- else {
- MindLesserFactor = 0;
+ else{
+ if((( WisInt - 199 ) / 2) > 0) {
+ MindLesserFactor = ( WisInt - 199 ) / 2;
+ }
+ else {
+ MindLesserFactor = 0;
+ }
+ MindFactor = WisInt - MindLesserFactor;
+ if(WisInt > 100) {
+ bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
+ }
+ else {
+ bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
+ }
}
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
+
bot_mana += (itembonuses.Mana + spellbonuses.Mana);
break;
case 'W':
WisInt = WIS;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ if (WisInt > 100) {
+ ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
+ if (WisInt > 201) {
+ ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
+ }
+ }
+ else {
+ ConvertedWisInt = WisInt;
+ }
+ if (GetLevel() < 41) {
+ wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
+ base_mana = (GetLevel() * 15);
+ }
+ else if (GetLevel() < 81) {
+ wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
+ base_mana = (600 + ((GetLevel() - 40) * 30));
+ }
+ else {
+ wisint_mana = (9 * ConvertedWisInt);
+ base_mana = (1800 + ((GetLevel() - 80) * 18));
+ }
+ bot_mana = base_mana + wisint_mana;
}
- else {
- MindLesserFactor = 0;
+ else{
+ if((( WisInt - 199 ) / 2) > 0) {
+ MindLesserFactor = ( WisInt - 199 ) / 2;
+ }
+ else {
+ MindLesserFactor = 0;
+ }
+ MindFactor = WisInt - MindLesserFactor;
+ if(WisInt > 100) {
+ bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
+ }
+ else {
+ bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
+ }
}
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
bot_mana += (itembonuses.Mana + spellbonuses.Mana);
break;
@@ -8147,7 +8490,9 @@
break;
}
- max_mana = cur_mana = bot_mana;
+ max_mana = bot_mana;
+
+ return bot_mana;
}
void Bot::GenerateSpecialAttacks() {
@@ -8275,52 +8620,562 @@
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
CalcMaxMana();
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
}
-sint32 Bot::CalcMaxHP() {
- int16 Post255 = 0;
- sint32 bot_hp = 0;
- int16 lm = GetClassLevelFactor();
+sint32 Bot::CalcHPRegenCap(){
+ int level = GetLevel();
+ sint32 hpregen_cap = 0;
+ hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25;
+ // Energetic Attunement AAs
+ uint8 botAAlevels = level - 70;
+ if(level >= 71)
+ // This AA is capped at 5 levels
+ botAAlevels = 5;
+ hpregen_cap += botAAlevels;
- if((STA-255)/2 > 0)
- Post255 = (STA-255)/2;
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Debug, "Bot::CalcHPRegenCap() called for %s - returning %d", GetName(), hpregen_cap);
+#endif
+ return hpregen_cap;
+}
+
+sint32 Bot::CalcManaRegenCap(){
+ int level = GetLevel();
+ sint32 manaregen_cap = 0;
+ switch(GetCasterClass())
+ {
+ case 'I':
+ manaregen_cap = RuleI(Character, ItemManaRegenCap) + (itembonuses.HeroicINT / 25);
+ break;
+ case 'W':
+ manaregen_cap = RuleI(Character, ItemManaRegenCap) + (itembonuses.HeroicWIS / 25);
+ break;
+ }
+ // Expansive Mind AAs
+ uint8 botAAlevels = 0;
+ if(level >= 70) {
+ botAAlevels += 5; // To match the bonuses that clients recieve and as there are only 5 levels available to clients, bots should only recieve the same.
+ }
+ else if(level >= 66) {
+ botAAlevels += level - 65;
+ }
+ manaregen_cap += botAAlevels;
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Debug, "Bot::CalcManaRegenCap() called for %s - returning %d", GetName(), manaregen_cap);
+#endif
+ return manaregen_cap;
+}
+
+// Return max stat value for level
+sint16 Bot::GetMaxStat() {
+ int level = GetLevel();
+ sint16 base = 0;
+
+ if (level < 61) {
+ base = 255;
+ }
+ else if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoF) {
+ base = 255 + 5 * (level - 60);
+ }
+ else if (level < 71) {
+ base = 255 + 5 * (level - 60);
+ }
+ else {
+ base = 330;
+ }
+
+ // Planar Power AAs, only 10 levels available to clients so bots should not receive more
+ uint8 botAAlevels = 0;
+ if(level >= 70) {
+ botAAlevels = 10;
+ }
+ else if(level >= 61) {
+ botAAlevels = level - 60;
+ }
+ base += botAAlevels * 5;
+
+ return(base);
+}
+
+sint16 Bot::GetMaxResist() {
+ int level = GetLevel();
+
+ sint16 base = 500;
+
+ if(level > 60)
+ base += ((level - 60) * 5);
+
+ // Discordant Defiance AAs, again only 5 levels for clients so only 5 for bots
+ uint8 botAAlevels = 0;
+ if(level >= 65) {
+ botAAlevels = 5;
+ }
+ else if(level >= 61) {
+ botAAlevels = level - 60;
+ }
+
+ base += botAAlevels * 5;
+ return base;
+}
+
+sint16 Bot::GetMaxSTR() {
+ return GetMaxStat()
+ + itembonuses.STRCapMod
+ + spellbonuses.STRCapMod;
+}
+sint16 Bot::GetMaxSTA() {
+ return GetMaxStat()
+ + itembonuses.STACapMod
+ + spellbonuses.STACapMod;
+}
+sint16 Bot::GetMaxDEX() {
+ return GetMaxStat()
+ + itembonuses.DEXCapMod
+ + spellbonuses.DEXCapMod;
+}
+sint16 Bot::GetMaxAGI() {
+ return GetMaxStat()
+ + itembonuses.AGICapMod
+ + spellbonuses.AGICapMod;
+}
+sint16 Bot::GetMaxINT() {
+ sint16 aaINTCapMod = 0;
+ // Innate Enlightenment AAs
+ if(this->IsBotINTCaster() && (level >= 61)) {
+ uint8 botAAlevels = 0;
+ if(level >= 65)
+ botAAlevels = 5;
+ else
+ botAAlevels = level - 60;
+ aaINTCapMod += botAAlevels * 10;
+ }
+
+ return GetMaxStat()
+ + itembonuses.INTCapMod
+ + spellbonuses.INTCapMod
+ + aaINTCapMod;
+}
+sint16 Bot::GetMaxWIS() {
+ sint16 aaWISCapMod = 0;
+ // Innate Enlightenment AAs
+ if(this->IsBotWISCaster() && (level >= 61)) {
+ uint8 botAAlevels = level - 60;
+ if(level >= 65)
+ botAAlevels = 5;
+ else
+ botAAlevels = level - 60;
+ aaWISCapMod += botAAlevels * 10;
+ }
+
+ return GetMaxStat()
+ + itembonuses.WISCapMod
+ + spellbonuses.WISCapMod
+ + aaWISCapMod;
+}
+sint16 Bot::GetMaxCHA() {
+ return GetMaxStat()
+ + itembonuses.CHACapMod
+ + spellbonuses.CHACapMod;
+}
+sint16 Bot::GetMaxMR() {
+ return GetMaxResist()
+ + itembonuses.MRCapMod
+ + spellbonuses.MRCapMod;
+}
+sint16 Bot::GetMaxPR() {
+ return GetMaxResist()
+ + itembonuses.PRCapMod
+ + spellbonuses.PRCapMod;
+}
+sint16 Bot::GetMaxDR() {
+ return GetMaxResist()
+ + itembonuses.DRCapMod
+ + spellbonuses.DRCapMod;
+}
+sint16 Bot::GetMaxCR() {
+ return GetMaxResist()
+ + itembonuses.CRCapMod
+ + spellbonuses.CRCapMod;
+}
+sint16 Bot::GetMaxFR() {
+ return GetMaxResist()
+ + itembonuses.FRCapMod
+ + spellbonuses.FRCapMod;
+}
+sint16 Bot::GetMaxCorrup() {
+ return GetMaxResist()
+ + itembonuses.CorrupCapMod
+ + spellbonuses.CorrupCapMod;
+}
+
+sint16 Bot::GetSTR() {
+ sint16 max = GetMaxSTR();
+ sint16 val = STR + itembonuses.STR + spellbonuses.STR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetSTA() {
+ sint16 max = GetMaxSTA();
+ sint16 val = STA + itembonuses.STA + spellbonuses.STA;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetDEX() {
+ sint16 max = GetMaxDEX();
+ sint16 val = DEX + itembonuses.DEX + spellbonuses.DEX;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetAGI() {
+ sint16 max = GetMaxAGI();
+ sint16 val = AGI + itembonuses.AGI + spellbonuses.AGI;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetINT() {
+ sint16 max = GetMaxINT();
+ sint16 val = INT + itembonuses.INT + spellbonuses.INT;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetWIS() {
+ sint16 max = GetMaxWIS();
+ sint16 val = WIS + itembonuses.WIS + spellbonuses.WIS;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCHA() {
+ sint16 max = GetMaxCHA();
+ sint16 val = CHA + itembonuses.CHA + spellbonuses.CHA;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetMR() {
+ sint16 max = GetMaxMR();
+ sint16 val = MR + itembonuses.MR + spellbonuses.MR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetFR() {
+ sint16 max = GetMaxFR();
+ sint16 val = FR + itembonuses.FR + spellbonuses.FR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetDR() {
+ sint16 max = GetMaxDR();
+ sint16 val = DR + itembonuses.DR + spellbonuses.DR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetPR() {
+ sint16 max = GetMaxPR();
+ sint16 val = PR + itembonuses.PR + spellbonuses.PR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCR() {
+ sint16 max = GetMaxCR();
+ sint16 val = CR + itembonuses.CR + spellbonuses.CR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCorrup() {
+ sint16 max = GetMaxCorrup();
+ sint16 val = Corrup + itembonuses.Corrup + spellbonuses.Corrup;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+
+void Bot::CalcRestState() {
+
+ // This method calculates rest state HP and mana regeneration.
+ // The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
+ // must be sitting down, and must not have any detrimental spells affecting them.
+ //
+ if(!RuleI(Character, RestRegenPercent))
+ return;
+
+ RestRegenHP = RestRegenMana = 0;
+
+ if(IsEngaged() || !IsSitting())
+ return;
+
+ if(!rest_timer.Check(false))
+ return;
+
+ uint32 buff_count = GetMaxTotalSlots();
+ for (unsigned int j = 0; j < buff_count; j++) {
+ if(buffs[j].spellid != SPELL_UNKNOWN) {
+ if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0))
+ if(!DetrimentalSpellAllowsRest(buffs[j].spellid))
+ return;
+ }
+ }
+
+ RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
+
+ RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
+}
+
+sint32 Bot::LevelRegen()
+{
+ int level = GetLevel();
+ bool bonus = GetRaceBitmask(_baseRace) & RuleI(Character, BaseHPRegenBonusRaces);
+ uint8 multiplier1 = bonus ? 2 : 1;
+ sint32 hp = 0;
+
+ //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities
+ if (level < 51) {
+ if (IsSitting()) {
+ if (level < 20)
+ hp += 2 * multiplier1;
+ else if (level < 50)
+ hp += 3 * multiplier1;
+ else //level == 50
+ hp += 4 * multiplier1;
+ }
+ else //feigned or standing
+ hp += 1 * multiplier1;
+ }
+ //there may be an easier way to calculate this next part, but I don't know what it is
+ else { //level >= 51
+ sint32 tmp = 0;
+ float multiplier2 = 1;
+ if (level < 56) {
+ tmp = 2;
+ if (bonus)
+ multiplier2 = 3;
+ }
+ else if (level < 60) {
+ tmp = 3;
+ if (bonus)
+ multiplier2 = 3.34;
+ }
+ else if (level < 61) {
+ tmp = 4;
+ if (bonus)
+ multiplier2 = 3;
+ }
+ else if (level < 63) {
+ tmp = 5;
+ if (bonus)
+ multiplier2 = 2.8;
+ }
+ else if (level < 65) {
+ tmp = 6;
+ if (bonus)
+ multiplier2 = 2.67;
+ }
+ else { //level >= 65
+ tmp = 7;
+ if (bonus)
+ multiplier2 = 2.58;
+ }
+
+ hp += sint32(float(tmp) * multiplier2);
+ }
+
+ return hp;
+}
+
+sint32 Bot::CalcHPRegen() {
+ sint32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen + (itembonuses.HeroicSTA / 25);
+ // Innate Regeneration AA
+ uint8 botAAlevels = 0;
+ if(level >= 51) {
+ if(level >= 53)
+ botAAlevels += 3;
+ else
+ botAAlevels += level - 50;
+ }
+ // Natural Healing AA
+ if(level >= 56) {
+ if(level >= 58)
+ botAAlevels += 3;
+ else
+ botAAlevels += level - 55;
+ }
+ // Body and Mind Rejuvenation AA
+ if(level >= 59)
+ botAAlevels += 1;
+
+ // Convalescence AA
+ if(level >= 61) {
+ if(level >= 62)
+ botAAlevels += 2;
+ else
+ botAAlevels += level - 60;
+ }
+ // Healthy Aura AA
+ if(level >= 66) {
+ if(level >= 70)
+ botAAlevels += 5;
+ else
+ botAAlevels += level - 65;
+ }
+
+ regen = ((regen + botAAlevels) * RuleI(Character, HPRegenMultiplier)) / 100;
+ return regen;
+}
+
+sint32 Bot::CalcManaRegen()
+{
+ uint8 level = GetLevel();
+ uint8 botclass = GetClass();
+ sint32 regen = 0;
+ //this should be changed so we dont med while camping, etc...
+ if (IsSitting())
+ {
+ BuffFadeBySitModifier();
+ if(botclass != WARRIOR && botclass != MONK && botclass != ROGUE && botclass != BERSERKER) {
+ regen = (((GetSkill(MEDITATE) / 10) + (level - (level / 4))) / 4) + 4;
+ regen += spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+ else
+ regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+ else {
+ regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+
+ uint8 botAAlevels = 0;
+ if(level >= 56) {
+ if(level >= 58)
+ botAAlevels = 3;
+ else
+ botAAlevels = level - 55;
+ }
+
+ regen += botAAlevels;
+
+ if(GetCasterClass() == 'I')
+ regen += (itembonuses.HeroicINT / 25);
+ else if(GetCasterClass() == 'W')
+ regen += (itembonuses.HeroicWIS / 25);
else
- Post255 = 0;
+ regen = 0;
- bot_hp = (5)+(GetLevel()*lm/10) + (((STA-Post255)*GetLevel()*lm/3000));
- bot_hp += itembonuses.HP;
+ regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+
+ float mana_regen_rate = RuleR(Bots, BotManaRegen);
+ if(mana_regen_rate < 0.0f)
+ mana_regen_rate = 0.0f;
+
+ regen = regen * mana_regen_rate; // 90% of people wouldnt guess that manaregen would decrease the larger the number they input, this makes more sense
+
+ return regen;
+}
+
+// This is for calculating Base HPs + STA bonus for SoD or later clients.
+int32 Bot::GetClassHPFactor() {
+
+ int factor;
+
+ // Note: Base HP factor under level 41 is equal to factor / 12, and from level 41 to 80 is factor / 6.
+ // Base HP over level 80 is factor / 10
+ // HP per STA point per level is factor / 30 for level 80+
+ // HP per STA under level 40 is the level 80 HP Per STA / 120, and for over 40 it is / 60.
+ switch(GetClass())
+ {
+ case DRUID:
+ case ENCHANTER:
+ case NECROMANCER:
+ case MAGICIAN:
+ case WIZARD:
+ factor = 240;
+ break;
+ case BEASTLORD:
+ case BERSERKER:
+ case MONK:
+ case ROGUE:
+ case SHAMAN:
+ factor = 255;
+ break;
+ case BARD:
+ case CLERIC:
+ factor = 264;
+ break;
+ case SHADOWKNIGHT:
+ case PALADIN:
+ factor = 288;
+ break;
+ case RANGER:
+ factor = 276;
+ break;
+ case WARRIOR:
+ factor = 300;
+ break;
+ default:
+ factor = 240;
+ break;
+ }
+ return factor;
+}
+
+sint32 Bot::CalcMaxHP() {
+ sint32 bot_hp = 0;
+
+ bot_hp += GenerateBaseHitPoints() + itembonuses.HP;
+
// Hitpoint AA's
int32 nd = 10000;
if(GetLevel() >= 69) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 5
- nd += 500;
- }
}
else if(GetLevel() >= 68) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 4
- nd += 400;
- }
}
else if(GetLevel() >= 67) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 3
- nd += 300;
- }
}
else if(GetLevel() >= 66) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 2
- nd += 200;
- }
}
else if(GetLevel() >= 65) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 1
- nd += 100;
- }
}
else if(GetLevel() >= 63) {
nd += 1500; // Planar Durablility AA 2
@@ -8343,8 +9198,19 @@
bot_hp = bot_hp * nd / 10000;
+ if(GetClass() == WARRIOR) { // Sturdiness AAs
+ if(GetLevel() >= 65){
+ uint8 botAAlevels = GetLevel() - 64;
+ if(botAAlevels > 5)
+ botAAlevels = 5;
+ bot_hp += botAAlevels * 100;
+ }
+ }
+
bot_hp += spellbonuses.HP;
+ bot_hp += bot_hp * (spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000;
+
max_hp = bot_hp;
return max_hp;
@@ -8694,59 +9560,7 @@
}
}
-// This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc.
-void Bot::CalcBotStats(bool showtext) {
- if(!GetBotOwner())
- return;
-
- if(showtext) {
- GetBotOwner()->Message(15, "Bot updating...");
- }
-
- if(!IsValidRaceClassCombo()) {
- GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel()));
- GetBotOwner()->Message(15, "Previous Bots Code releases did not check Race/Class combinations during create.");
- GetBotOwner()->Message(15, "Unless you are experiencing heavy lag, you should delete and remake this bot.");
- }
-
- if(GetBotOwner()->GetLevel() != GetLevel())
- SetLevel(GetBotOwner()->GetLevel());
-
- GenerateBaseStats();
-
- GenerateAABonuses();
-
- GenerateArmorClass();
-
- //// Calc Base Hit Points
- //int16 lm = GetClassLevelFactor();
- //int16 Post255;
- //if((bsta-255)/2 > 0)
- // Post255 = (bsta-255)/2;
- //else
- // Post255 = 0;
- //sint32 bot_hp = (5)+(blevel*lm/10) + (((bsta-Post255)*blevel*lm/3000)) + ((Post255*blevel)*lm/6000);
- GenerateBaseHitPoints();
-
- GenerateBaseManaPoints();
-
- GenerateSpecialAttacks();
-
- if(showtext) {
- GetBotOwner()->Message(15, "Base stats:");
- GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), base_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
- GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
- }
-
- // Let's find the items in the bot inventory
- sint32 items_hp = 0;
- sint32 items_mana = 0;
-
- /*if(this->Save())
- this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName());
- else
- this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());*/
-
+void Bot::CalcItemBonuses() {
memset(&itembonuses, 0, sizeof(StatBonuses));
const Item_Struct* itemtmp = 0;
@@ -8887,47 +9701,66 @@
}
}
- MR += itembonuses.MR;
- CR += itembonuses.CR;
- DR += itembonuses.DR;
- FR += itembonuses.FR;
- PR += itembonuses.PR;
- Corrup += itembonuses.Corrup;
- AC += itembonuses.AC;
- STR += itembonuses.STR;
- STA += itembonuses.STA;
- DEX += itembonuses.DEX;
- AGI += itembonuses.AGI;
- INT += itembonuses.INT;
- WIS += itembonuses.WIS;
- CHA += itembonuses.CHA;
- ATK += itembonuses.ATK;
+ if(itembonuses.HPRegen > CalcHPRegenCap())
+ itembonuses.HPRegen = CalcHPRegenCap();
- MR += spellbonuses.MR;
- CR += spellbonuses.CR;
- DR += spellbonuses.DR;
- FR += spellbonuses.FR;
- PR += spellbonuses.PR;
- Corrup += spellbonuses.Corrup;
- AC += spellbonuses.AC;
- STR += spellbonuses.STR;
- STA += spellbonuses.STA;
- DEX += spellbonuses.DEX;
- AGI += spellbonuses.AGI;
- INT += spellbonuses.INT;
- WIS += spellbonuses.WIS;
- CHA += spellbonuses.CHA;
- ATK += spellbonuses.ATK;
+ if(itembonuses.ManaRegen > CalcManaRegenCap())
+ itembonuses.ManaRegen = CalcManaRegenCap();
+}
- cur_hp = CalcMaxHP();
- GenerateBaseManaPoints();
+// This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc.
+void Bot::CalcBotStats(bool showtext) {
+ if(!GetBotOwner())
+ return;
+
+ if(showtext) {
+ GetBotOwner()->Message(15, "Bot updating...");
+ }
+ if(!IsValidRaceClassCombo()) {
+ GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel()));
+ GetBotOwner()->Message(15, "Previous Bots Code releases did not check Race/Class combinations during create.");
+ GetBotOwner()->Message(15, "Unless you are experiencing heavy lag, you should delete and remake this bot.");
+ }
+
+ if(GetBotOwner()->GetLevel() != GetLevel())
+ SetLevel(GetBotOwner()->GetLevel());
+
+ GenerateBaseStats();
+
+ GenerateAABonuses();
+
+ GenerateSpecialAttacks();
+
+ if(showtext) {
+ GetBotOwner()->Message(15, "Base stats:");
+ GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), base_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
+ GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
+ }
+
+ // Let's find the items in the bot inventory
+
+ /*if(this->Save())
+ this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName());
+ else
+ this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());*/
+
+ CalcItemBonuses();
+ CalcSpellBonuses(&spellbonuses);
+ GenerateArmorClass();
+
+ CalcMaxHP();
+ CalcMaxMana();
+
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
AI_AddNPCSpells(this->GetBotSpellID());
if(showtext) {
GetBotOwner()->Message(15, "I'm updated.");
- GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), max_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
- GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
+ GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), max_hp, GetAC(), max_mana, GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA());
+ GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",GetMR(),GetPR(),GetFR(),GetCR(),GetDR(),GetCorrup ());
}
}
botspellsai.cpp
Index: botspellsai.cpp
================================================== =================
--- botspellsai.cpp (revision 1832)
+++ botspellsai.cpp (working copy)
@@ -209,8 +209,7 @@
break;
}
case SpellType_Nuke: {
- if(((MakeRandomInt(1, 100) <= iChance) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
- && ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER))))
+ if((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
{
if(!checked_los) {
if(!CheckLosFN(tar))
@@ -228,7 +227,17 @@
else if(botClass == WIZARD) {
botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar);
}
-
+
+ if(botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER) {
+ if(botSpell.SpellId == 0){
+ int8 stunChance = (tar->IsCasting() ? 30: 15);
+
+ if(!tar->IsStunned() && ( botClass == PALADIN || (MakeRandomInt(1, 100) <= stunChance))){
+ botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target);
+ }
+ }
+ }
+
if(botSpell.SpellId == 0)
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target);
@@ -1207,7 +1214,7 @@
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
- if(IsPureNukeSpell(botSpellListItr->SpellId)) {
+ if(IsPureNukeSpell(botSpellListItr->SpellId) && IsDamageSpell(botSpellListItr->SpellId)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -1220,6 +1227,30 @@
return result;
}
+BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) {
+ BotSpell result;
+
+ result.SpellId = 0;
+ result.SpellIndex = 0;
+ result.ManaCost = 0;
+
+ if(botCaster) {
+ std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType);
+
+ for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
+ // Assuming all the spells have been loaded into this list by level and in descending order
+ if(IsStunSpell(botSpellListItr->SpellId)) {
+ result.SpellId = botSpellListItr->SpellId;
+ result.SpellIndex = botSpellListItr->SpellIndex;
+ result.ManaCost = botSpellListItr->ManaCost;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) {
BotSpell result;
1- Hybrid bots were casting healing spells on mobs instead of nukes. This has been fixed.
2- Classes who can cast stun spells now will.
1746_optional_sql_bot_manaregen.sql
UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen';
mob.cpp
Index: mob.cpp
================================================== =================
--- mob.cpp (revision 1832)
+++ mob.cpp (working copy)
@@ -4182,7 +4182,9 @@
israidgrouped = false;
}
isgrouped = v;
- parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+ if(IsClient())
+ parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
}
void Mob::SetRaidGrouped(bool v)
@@ -4192,7 +4194,9 @@
isgrouped = false;
}
israidgrouped = v;
- parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+ if(IsClient())
+ parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
}
sint16 Mob::GetCriticalChanceBonus(int16 skill, bool aa_bonus)
bot.h
Index: bot.h
================================================== =================
--- bot.h (revision 1832)
+++ bot.h (working copy)
@@ -99,7 +99,8 @@
virtual void TryCriticalHit(Mob *defender, int16 skill, sint32 &damage);
virtual bool TryFinishingBlow(Mob *defender, SkillType skillinuse);
virtual void DoRiposte(Mob* defender);
- inline virtual sint16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); }
+ inline virtual sint16 GetATK();
+ uint16 GetPrimarySkillValue();
virtual void MeleeMitigation(Mob *attacker, sint32 &damage, sint32 minhit);
virtual void DoSpecialAttackDamage(Mob *who, SkillType skill, sint32 max_damage, sint32 min_damage = 1, sint32 hate_override = -1);
virtual void TryBackstab(Mob *other);
@@ -109,6 +110,7 @@
virtual bool TryHeadShot(Mob* defender, SkillType skillInUse);
virtual sint32 CheckAggroAmount(int16 spellid);
virtual void CalcBonuses();
+ void CalcItemBonuses();
virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
inline virtual bool IsPet() { return false; }
@@ -119,6 +121,7 @@
virtual sint32 CheckHealAggroAmount(int16 spellid, int32 heal_possible = 0);
virtual sint32 CalcMaxMana();
virtual void SetAttackTimer();
+ int32 GetClassHPFactor();
virtual sint32 CalcMaxHP();
bool DoFinishedSpellAETarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
@@ -137,6 +140,27 @@
bool IsStanding();
bool IsBotCasterCombatRange(Mob *target);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ;
+ inline virtual sint16 GetMaxStat();
+ inline virtual sint16 GetMaxResist();
+ inline virtual sint16 GetMaxSTR();
+ inline virtual sint16 GetMaxSTA();
+ inline virtual sint16 GetMaxDEX();
+ inline virtual sint16 GetMaxAGI();
+ inline virtual sint16 GetMaxINT();
+ inline virtual sint16 GetMaxWIS();
+ inline virtual sint16 GetMaxCHA();
+ inline virtual sint16 GetMaxMR();
+ inline virtual sint16 GetMaxPR();
+ inline virtual sint16 GetMaxDR();
+ inline virtual sint16 GetMaxCR();
+ inline virtual sint16 GetMaxFR();
+ inline virtual sint16 GetMaxCorrup();
+ sint32 CalcHPRegenCap();
+ sint32 CalcManaRegenCap();
+ sint32 LevelRegen();
+ sint32 CalcHPRegen();
+ sint32 CalcManaRegen();
+ void CalcRestState();
// AI Methods
virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
@@ -233,6 +257,7 @@
static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster);
static std::string GetBotMagicianPetType(Bot* botCaster);
static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType);
+ static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType);
static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target);
static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
@@ -263,6 +288,57 @@
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
BotRoleType GetBotRole() { return _botRole; }
bool IsBotCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+ bool IsBotINTCaster() { return (GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+ bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); }
+ inline virtual sint16 GetAC() { return AC; }
+ inline virtual sint16 GetSTR();
+ inline virtual sint16 GetSTA();
+ inline virtual sint16 GetDEX();
+ inline virtual sint16 GetAGI();
+ inline virtual sint16 GetINT();
+ inline virtual sint16 GetWIS();
+ inline virtual sint16 GetCHA();
+ inline virtual sint16 GetMR();
+ inline virtual sint16 GetFR();
+ inline virtual sint16 GetDR();
+ inline virtual sint16 GetPR();
+ inline virtual sint16 GetCR();
+ inline virtual sint16 GetCorrup();
+ //Heroic
+ inline virtual sint16 GetHeroicSTR() const { return itembonuses.HeroicSTR; }
+ inline virtual sint16 GetHeroicSTA() const { return itembonuses.HeroicSTA; }
+ inline virtual sint16 GetHeroicDEX() const { return itembonuses.HeroicDEX; }
+ inline virtual sint16 GetHeroicAGI() const { return itembonuses.HeroicAGI; }
+ inline virtual sint16 GetHeroicINT() const { return itembonuses.HeroicINT; }
+ inline virtual sint16 GetHeroicWIS() const { return itembonuses.HeroicWIS; }
+ inline virtual sint16 GetHeroicCHA() const { return itembonuses.HeroicCHA; }
+ inline virtual sint16 GetHeroicMR() const { return itembonuses.HeroicMR; }
+ inline virtual sint16 GetHeroicFR() const { return itembonuses.HeroicFR; }
+ inline virtual sint16 GetHeroicDR() const { return itembonuses.HeroicDR; }
+ inline virtual sint16 GetHeroicPR() const { return itembonuses.HeroicPR; }
+ inline virtual sint16 GetHeroicCR() const { return itembonuses.HeroicCR; }
+ inline virtual sint16 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; }
+ // Mod2
+ inline virtual sint16 GetShielding() const { return itembonuses.MeleeMitigation; }
+ inline virtual sint16 GetSpellShield() const { return itembonuses.SpellShield; }
+ inline virtual sint16 GetDoTShield() const { return itembonuses.DoTShielding; }
+ inline virtual sint16 GetStunResist() const { return itembonuses.StunResist; }
+ inline virtual sint16 GetStrikeThrough() const { return itembonuses.StrikeThrough; }
+ inline virtual sint16 GetAvoidance() const { return itembonuses.AvoidMeleeChance; }
+ inline virtual sint16 GetAccuracy() const { return itembonuses.HitChance; }
+ inline virtual sint16 GetCombatEffects() const { return itembonuses.ProcChance; }
+ inline virtual sint16 GetDS() const { return itembonuses.DamageShield; }
+ // Mod3
+ inline virtual sint16 GetHealAmt() const { return itembonuses.HealAmt; }
+ inline virtual sint16 GetSpellDmg() const { return itembonuses.SpellDmg; }
+ inline virtual sint16 GetClair() const { return itembonuses.Clairvoyance; }
+ inline virtual sint16 GetDSMit() const { return itembonuses.DSMitigation; }
+
+ inline virtual sint16 GetSingMod() const { return itembonuses.singingMod; }
+ inline virtual sint16 GetBrassMod() const { return itembonuses.brassMod; }
+ inline virtual sint16 GetPercMod() const { return itembonuses.percussionMod; }
+ inline virtual sint16 GetStringMod() const { return itembonuses.stringedMod; }
+ inline virtual sint16 GetWindMod() const { return itembonuses.windMod; }
// "SET" Class Methods
void SetBotSpellID(uint32 newSpellID);
@@ -315,6 +391,9 @@
int32 _lastZoneId;
bool _rangerAutoWeaponSelect;
BotRoleType _botRole;
+ unsigned int RestRegenHP;
+ unsigned int RestRegenMana;
+ Timer rest_timer;
// Private "base stats" Members
sint16 _baseMR;
@@ -336,12 +415,13 @@
int8 _baseGender; // Bots gender. Necessary to preserve the original value otherwise it can be changed by illusions.
// Class Methods
+ sint16 acmod();
void GenerateBaseStats();
void GenerateAppearance();
void GenerateArmorClass();
- void GenerateBaseHitPoints();
+ sint32 GenerateBaseHitPoints();
void GenerateAABonuses();
- void GenerateBaseManaPoints();
+ sint32 GenerateBaseManaPoints();
void GenerateSpecialAttacks();
void SetBotID(uint32 botID);
bool CalcBotHitChance(Mob* target, SkillType skillinuse, int Hand);
bot.cpp
Index: bot.cpp
================================================== =================
--- bot.cpp (revision 1832)
+++ bot.cpp (working copy)
@@ -5,7 +5,7 @@
#include "doors.h"
// This constructor is used during the bot create command
-Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false) {
+Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) {
if(botOwner) {
this->SetBotOwner(botOwner);
this->_botOwnerCharacterID = botOwner->CharacterID();
@@ -39,6 +39,8 @@
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
+ RestRegenHP = 0;
+ RestRegenMana = 0;
SetBotID(0);
SetBotSpellID(0);
@@ -48,6 +50,8 @@
SetPetChooser(false);
SetRangerAutoWeaponSelect(false);
+ rest_timer.Disable();
+
SetFollowDistance(184);
// Do this once and only in this constructor
@@ -57,13 +61,17 @@
GenerateArmorClass();
// Calculate HitPoints Last As It Uses Base Stats
- GenerateBaseHitPoints();
+ cur_hp = GenerateBaseHitPoints();
+ cur_mana = GenerateBaseManaPoints();
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
strcpy(this->name, this->GetCleanName());
}
// This constructor is used when the bot is loaded out of the database
-Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, int32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false) {
+Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, int32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) {
this->_botOwnerCharacterID = botOwnerCharacterID;
if(this->_botOwnerCharacterID > 0) {
@@ -94,6 +102,8 @@
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
+ RestRegenHP = 0;
+ RestRegenMana = 0;
SetBotID(botID);
SetBotSpellID(botSpellsID);
@@ -103,6 +113,8 @@
SetPetChooser(false);
SetRangerAutoWeaponSelect(false);
+ rest_timer.Disable();
+
SetFollowDistance(184);
strcpy(this->name, this->GetCleanName());
@@ -120,12 +132,17 @@
}
GenerateBaseStats();
- GenerateArmorClass();
- // Calculate HitPoints Last As It Uses Base Stats
- GenerateBaseHitPoints();
+ // Load saved buffs
+ LoadBuffs();
CalcBotStats(false);
+
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
+ cur_hp = max_hp;
+ cur_mana = max_mana;
}
Bot::~Bot() {
@@ -409,7 +426,7 @@
Wisdom += 15;
Charisma += 10;
Dexterity += 5;
- Attack =+ 17;
+ Attack += 17;
DiseaseResist += 8;
break;
case 4: // Ranger
@@ -763,65 +780,534 @@
}
+sint16 Bot::acmod() {
+ int agility = GetAGI();
+ int level = GetLevel();
+ if(agility < 1 || level < 1)
+ return(0);
+
+ if (agility <=74){
+ if (agility == 1)
+ return -24;
+ else if (agility <=3)
+ return -23;
+ else if (agility == 4)
+ return -22;
+ else if (agility <=6)
+ return -21;
+ else if (agility <=8)
+ return -20;
+ else if (agility == 9)
+ return -19;
+ else if (agility <=11)
+ return -18;
+ else if (agility == 12)
+ return -17;
+ else if (agility <=14)
+ return -16;
+ else if (agility <=16)
+ return -15;
+ else if (agility == 17)
+ return -14;
+ else if (agility <=19)
+ return -13;
+ else if (agility == 20)
+ return -12;
+ else if (agility <=22)
+ return -11;
+ else if (agility <=24)
+ return -10;
+ else if (agility == 25)
+ return -9;
+ else if (agility <=27)
+ return -8;
+ else if (agility == 28)
+ return -7;
+ else if (agility <=30)
+ return -6;
+ else if (agility <=32)
+ return -5;
+ else if (agility == 33)
+ return -4;
+ else if (agility <=35)
+ return -3;
+ else if (agility == 36)
+ return -2;
+ else if (agility <=38)
+ return -1;
+ else if (agility <=65)
+ return 0;
+ else if (agility <=70)
+ return 1;
+ else if (agility <=74)
+ return 5;
+ }
+ else if(agility <= 137) {
+ if (agility == 75){
+ if (level <= 6)
+ return 9;
+ else if (level <= 19)
+ return 23;
+ else if (level <= 39)
+ return 33;
+ else
+ return 39;
+ }
+ else if (agility >= 76 && agility <= 79){
+ if (level <= 6)
+ return 10;
+ else if (level <= 19)
+ return 23;
+ else if (level <= 39)
+ return 33;
+ else
+ return 40;
+ }
+ else if (agility == 80){
+ if (level <= 6)
+ return 11;
+ else if (level <= 19)
+ return 24;
+ else if (level <= 39)
+ return 34;
+ else
+ return 41;
+ }
+ else if (agility >= 81 && agility <= 85){
+ if (level <= 6)
+ return 12;
+ else if (level <= 19)
+ return 25;
+ else if (level <= 39)
+ return 35;
+ else
+ return 42;
+ }
+ else if (agility >= 86 && agility <= 90){
+ if (level <= 6)
+ return 12;
+ else if (level <= 19)
+ return 26;
+ else if (level <= 39)
+ return 36;
+ else
+ return 42;
+ }
+ else if (agility >= 91 && agility <= 95){
+ if (level <= 6)
+ return 13;
+ else if (level <= 19)
+ return 26;
+ else if (level <= 39)
+ return 36;
+ else
+ return 43;
+ }
+ else if (agility >= 96 && agility <= 99){
+ if (level <= 6)
+ return 14;
+ else if (level <= 19)
+ return 27;
+ else if (level <= 39)
+ return 37;
+ else
+ return 44;
+ }
+ else if (agility == 100 && level >= 7){
+ if (level <= 19)
+ return 28;
+ else if (level <= 39)
+ return 38;
+ else
+ return 45;
+ }
+ else if (level <= 6) {
+ return 15;
+ }
+ //level is >6
+ else if (agility >= 101 && agility <= 105){
+ if (level <= 19)
+ return 29;
+ else if (level <= 39)
+ return 39;// not verified
+ else
+ return 45;
+ }
+ else if (agility >= 106 && agility <= 110){
+ if (level <= 19)
+ return 29;
+ else if (level <= 39)
+ return 39;// not verified
+ else
+ return 46;
+ }
+ else if (agility >= 111 && agility <= 115){
+ if (level <= 19)
+ return 30;
+ else if (level <= 39)
+ return 40;// not verified
+ else
+ return 47;
+ }
+ else if (agility >= 116 && agility <= 119){
+ if (level <= 19)
+ return 31;
+ else if (level <= 39)
+ return 41;
+ else
+ return 47;
+ }
+ else if (level <= 19) {
+ return 32;
+ }
+ //level is > 19
+ else if (agility == 120){
+ if (level <= 39)
+ return 42;
+ else
+ return 48;
+ }
+ else if (agility <= 125){
+ if (level <= 39)
+ return 42;
+ else
+ return 49;
+ }
+ else if (agility <= 135){
+ if (level <= 39)
+ return 42;
+ else
+ return 50;
+ }
+ else {
+ if (level <= 39)
+ return 42;
+ else
+ return 51;
+ }
+ } else if(agility <= 300) {
+ if(level <= 6) {
+ if(agility <= 139)
+ return(21);
+ else if(agility == 140)
+ return(22);
+ else if(agility <= 145)
+ return(23);
+ else if(agility <= 150)
+ return(23);
+ else if(agility <= 155)
+ return(24);
+ else if(agility <= 159)
+ return(25);
+ else if(agility == 160)
+ return(26);
+ else if(agility <= 165)
+ return(26);
+ else if(agility <= 170)
+ return(27);
+ else if(agility <= 175)
+ return(28);
+ else if(agility <= 179)
+ return(28);
+ else if(agility == 180)
+ return(29);
+ else if(agility <= 185)
+ return(30);
+ else if(agility <= 190)
+ return(31);
+ else if(agility <= 195)
+ return(31);
+ else if(agility <= 199)
+ return(32);
+ else if(agility <= 219)
+ return(33);
+ else if(agility <= 239)
+ return(34);
+ else
+ return(35);
+ } else if(level <= 19) {
+ if(agility <= 139)
+ return(34);
+ else if(agility == 140)
+ return(35);
+ else if(agility <= 145)
+ return(36);
+ else if(agility <= 150)
+ return(37);
+ else if(agility <= 155)
+ return(37);
+ else if(agility <= 159)
+ return(38);
+ else if(agility == 160)
+ return(39);
+ else if(agility <= 165)
+ return(40);
+ else if(agility <= 170)
+ return(40);
+ else if(agility <= 175)
+ return(41);
+ else if(agility <= 179)
+ return(42);
+ else if(agility == 180)
+ return(43);
+ else if(agility <= 185)
+ return(43);
+ else if(agility <= 190)
+ return(44);
+ else if(agility <= 195)
+ return(45);
+ else if(agility <= 199)
+ return(45);
+ else if(agility <= 219)
+ return(46);
+ else if(agility <= 239)
+ return(47);
+ else
+ return(48);
+ } else if(level <= 39) {
+ if(agility <= 139)
+ return(44);
+ else if(agility == 140)
+ return(45);
+ else if(agility <= 145)
+ return(46);
+ else if(agility <= 150)
+ return(47);
+ else if(agility <= 155)
+ return(47);
+ else if(agility <= 159)
+ return(48);
+ else if(agility == 160)
+ return(49);
+ else if(agility <= 165)
+ return(50);
+ else if(agility <= 170)
+ return(50);
+ else if(agility <= 175)
+ return(51);
+ else if(agility <= 179)
+ return(52);
+ else if(agility == 180)
+ return(53);
+ else if(agility <= 185)
+ return(53);
+ else if(agility <= 190)
+ return(54);
+ else if(agility <= 195)
+ return(55);
+ else if(agility <= 199)
+ return(55);
+ else if(agility <= 219)
+ return(56);
+ else if(agility <= 239)
+ return(57);
+ else
+ return(58);
+ } else { //lvl >= 40
+ if(agility <= 139)
+ return(51);
+ else if(agility == 140)
+ return(52);
+ else if(agility <= 145)
+ return(53);
+ else if(agility <= 150)
+ return(53);
+ else if(agility <= 155)
+ return(54);
+ else if(agility <= 159)
+ return(55);
+ else if(agility == 160)
+ return(56);
+ else if(agility <= 165)
+ return(56);
+ else if(agility <= 170)
+ return(57);
+ else if(agility <= 175)
+ return(58);
+ else if(agility <= 179)
+ return(58);
+ else if(agility == 180)
+ return(59);
+ else if(agility <= 185)
+ return(60);
+ else if(agility <= 190)
+ return(61);
+ else if(agility <= 195)
+ return(61);
+ else if(agility <= 199)
+ return(62);
+ else if(agility <= 219)
+ return(63);
+ else if(agility <= 239)
+ return(64);
+ else
+ return(65);
+ }
+ }
+ else{
+ //seems about 21 agil per extra AC pt over 300...
+ return (65 + ((agility-300) / 21));
+ }
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Error, "Error in Bot::acmod(): Agility: %i, Level: %i",agility,level);
+#endif
+ return 0;
+}
+
void Bot::GenerateArmorClass() {
- // Base AC
- int bac = GetAC();
- switch(this->GetClass()) {
- case WARRIOR:
- case SHADOWKNIGHT:
- case PALADIN:
- bac = bac*1.5;
+ /// new formula
+ int avoidance = 0;
+ avoidance = (acmod() + ((GetSkill(DEFENSE)*16)/9));
+ if (avoidance < 0)
+ avoidance = 0;
+
+ int mitigation = 0;
+ if (GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) {
+ mitigation = GetSkill(DEFENSE)/4 + (itembonuses.AC+1);
+ mitigation -= 4;
+ } else {
+ mitigation = GetSkill(DEFENSE)/3 + ((itembonuses.AC*4)/3);
+ if(GetClass() == MONK)
+ mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close...
}
+ int displayed = 0;
+ displayed += ((avoidance+mitigation)*1000)/847; //natural AC
+
+ //Iksar AC, untested
+ if (GetRace() == IKSAR) {
+ displayed += 12;
+ int iksarlevel = GetLevel();
+ iksarlevel -= 10;
+ if (iksarlevel > 25)
+ iksarlevel = 25;
+ if (iksarlevel > 0)
+ displayed += iksarlevel * 12 / 10;
+ }
+
+ //spell AC bonuses are added directly to natural total
+ displayed += spellbonuses.AC;
- this->AC = bac;
+ this->AC = displayed;
}
-void Bot::GenerateBaseHitPoints() {
- // Calc Base Hit Points
- /*int16 multiplier = 1;
- switch(this->GetClass()) {
- case WARRIOR:
- multiplier = 220;
+uint16 Bot::GetPrimarySkillValue()
+{
+ SkillType skill = HIGHEST_SKILL; //because NULL == 0, which is 1H Slashing, & we want it to return 0 from GetSkill
+ bool equiped = m_inv.GetItem(SLOT_PRIMARY);
+
+ if (!equiped)
+ skill = HAND_TO_HAND;
+ else {
+
+ uint8 type = m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType; //is this the best way to do this?
+
+ switch (type)
+ {
+ case ItemType1HS: // 1H Slashing
+ {
+ skill = _1H_SLASHING;
break;
- case DRUID:
- case CLERIC:
- case SHAMAN:
- multiplier = 150;
+ }
+ case ItemType2HS: // 2H Slashing
+ {
+ skill = _2H_SLASHING;
break;
- case BERSERKER:
- case PALADIN:
- case SHADOWKNIGHT:
- multiplier = 210;
+ }
+ case ItemTypePierce: // Piercing
+ {
+ skill = PIERCING;
break;
- case MONK:
- case BARD:
- case ROGUE:
- case BEASTLORD:
- multiplier = 180;
+ }
+ case ItemType1HB: // 1H Blunt
+ {
+ skill = _1H_BLUNT;
break;
- case RANGER:
- multiplier = 200;
+ }
+ case ItemType2HB: // 2H Blunt
+ {
+ skill = _2H_BLUNT;
break;
- case MAGICIAN:
- case WIZARD:
- case NECROMANCER:
- case ENCHANTER:
- multiplier = 120;
+ }
+ case ItemType2HPierce: // 2H Piercing
+ {
+ skill = PIERCING;
break;
+ }
+ case ItemTypeHand2Hand: // Hand to Hand
+ {
+ skill = HAND_TO_HAND;
+ break;
+ }
+ default: // All other types default to Hand to Hand
+ {
+ skill = HAND_TO_HAND;
+ break;
+ }
+ }
}
- int16 lm = multiplier;*/
+
+ return GetSkill(skill);
+}
+
+sint16 Bot::GetATK() {
+ int16 AttackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ if(WornCap > (RuleI(Character, ItemATKCap) + spellbonuses.ATK)) // GetATKBonus returns item and spell bonuses, so to keep under cap need to take into account
+ WornCap = (RuleI(Character, ItemATKCap) + spellbonuses.ATK);
+
+ AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69));
+
+ if (AttackRating < 10)
+ AttackRating = 10;
+
+ return AttackRating;
+}
+
+sint32 Bot::GenerateBaseHitPoints() {
+ // Calc Base Hit Points
+ int new_base_hp = 0;
int16 lm = GetClassLevelFactor();
int16 Post255;
+ int16 NormalSTA = GetSTA();
- if((this->GetSTA()-255)/2 > 0)
- Post255 = (this->GetSTA()-255)/2;
- else
- Post255 = 0;
+ if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ float SoDPost255;
+
+ if(((NormalSTA - 255) / 2) > 0)
+ SoDPost255 = ((NormalSTA - 255) / 2);
+ else
+ SoDPost255 = 0;
- int new_base_hp = (5)+(GetLevel()*lm/10) + (((this->GetSTA()-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000);
+ int hp_factor = GetClassHPFactor();
+
+ if (level < 41) {
+ new_base_hp = (5 + (GetLevel() * hp_factor / 12) +
+ ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600));
+ }
+ else if (level < 81) {
+ new_base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800));
+ }
+ else {
+ new_base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * hp_factor / 45));
+ }
+ }
+ else{
+ if((NormalSTA-255)/2 > 0)
+ Post255 = (NormalSTA-255)/2;
+ else
+ Post255 = 0;
+ new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000);
+ }
+
this->base_hp = new_base_hp;
- this->cur_hp = this->base_hp;
+
+ return new_base_hp;
}
void Bot::GenerateAABonuses() {
@@ -830,6 +1316,8 @@
if(botlevel >= 51) {
//level 51 = 1 AA level
uint8 botAAlevels = botlevel - 50;
+ if(botAAlevels > 15)
+ botAAlevels = 15;
STR += botAAlevels * 2; // Innate Strength AAs
STA += botAAlevels * 2; // Innate Stamina AAs
AGI += botAAlevels * 2; // Innate Agility AAs
@@ -1252,6 +1740,7 @@
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
buffs[BuffCount].casterAARank = atoi(DataRow[12]);
+ buffs[BuffCount].casterid = 0;
bool IsPersistent = false;
@@ -1658,48 +2147,28 @@
//6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting
if(!IsMoving() && !IsEngaged()) {
SendPosition();
+
+ if(IsSitting()){
+ if(!rest_timer.Enabled()){
+ rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
+ }
+ }
}
SpellProcess();
BuffProcess();
+ CalcRestState();
+
if(curfp)
ProcessFlee();
- int32 bonus = 0;
-
- // Med is meditating
- if(IsSitting())
- bonus += 3;
-
- //sint32 OOCRegen = 0;
- //if(oocregen > 0){ //should pull from Mob class
- // OOCRegen += GetMaxHP() * oocregen / 100;
- //}
-
- //Lieka Edit: Fixing NPC regen. NPCs should regen to full during a set duration, not based on their HPs. Increase NPC's HPs by % of total HPs / tick.
- //if((GetHP() < GetMaxHP()) && !IsPet()) {
- // if(!IsEngaged()) {//NPC out of combat
- // if(hp_regen > OOCRegen)
- // SetHP(GetHP() + hp_regen);
- // else
- // SetHP(GetHP() + OOCRegen);
- // } else
- // SetHP(GetHP()+hp_regen);
- //} else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) {
- // if(!IsEngaged()) //pet
- // SetHP(GetHP()+hp_regen+bonus+(GetLevel()/5));
- // else
- // SetHP(GetHP()+hp_regen+bonus);
- //} else
- // SetHP(GetHP()+hp_regen);
-
if(GetHP() < GetMaxHP())
- SetHP(GetHP() + hp_regen + bonus);
+ SetHP(GetHP() + CalcHPRegen() + RestRegenHP);
if(GetMana() < GetMaxMana())
- SetMana(GetMana() + mana_regen + bonus);
+ SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
}
if (sendhpupdate_timer.Check()) {
@@ -1731,128 +2200,27 @@
if(isSitting) {
// If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate
if(GetManaRatio() < 99.0f) {
- if(mana_timer.Check(true)) {
- SetAppearance(eaSitting, false);
- /*if(!((int)GetManaRatio() % 24)) {
- Say("Medding for Mana. I have %3.1f%% of %d mana. It is: %d", GetManaRatio(), GetMaxMana(), GetMana());
- }*/
- int32 level = GetLevel();
- int32 regen = (((GetSkill(MEDITATE)/10)+(level-(level/4)))/4)+4;
- spellbonuses.ManaRegen = 0;
- for(int j=0; j<BUFF_COUNT; j++) {
- if(buffs[j].spellid != 65535) {
- const SPDat_Spell_Struct &spell = spells[buffs[j].spellid];
- for(int i=0; i<EFFECT_COUNT; i++) {
- if(IsBlankSpellEffect(buffs[j].spellid, i))
- continue;
- int effect = spell.effectid[i];
- switch(effect) {
- case SE_CurrentMana:
- spellbonuses.ManaRegen += CalcSpellEffectValue(buffs[j].spellid, i, buffs[j].casterlevel);
- break;
- }
- }
- }
- }
- regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen);
- if(level >= 55) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 56) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 57) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 71) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 72) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 73) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 74) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 75) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
-
- float mana_regen_rate = RuleR(Bots, BotManaRegen);
- if(mana_regen_rate < 1.0f)
- mana_regen_rate = 1.0f;
-
- regen = regen / mana_regen_rate;
-
- SetMana(GetMana() + regen);
- }
+ if(!IsSitting())
+ Sit();
}
else {
- SetAppearance(eaStanding, false);
+ if(IsSitting())
+ Stand();
}
}
else {
- // Let's check our mana in fights..
- if(mana_timer.Check(true)) {
- /*if((!((int)GetManaRatio() % 12)) && ((int)GetManaRatio() < 10)) {
- Say("Medding for Mana. I have %3.1f%% of %d mana. It is: %d", GetManaRatio(), GetMaxMana(), GetMana());
- }*/
- int32 level = GetLevel();
- spellbonuses.ManaRegen = 0;
- for(int j=0; j<BUFF_COUNT; j++) {
- if(buffs[j].spellid != 65535) {
- const SPDat_Spell_Struct &spell = spells[buffs[j].spellid];
- for(int i=0; i<EFFECT_COUNT; i++) {
- if(IsBlankSpellEffect(buffs[j].spellid, i))
- continue;
- int effect = spell.effectid[i];
- switch(effect) {
- case SE_CurrentMana:
- spellbonuses.ManaRegen += CalcSpellEffectValue(buffs[j].spellid, i, buffs[j].casterlevel);
- break;
- }
- }
- }
- }
- int32 regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen + (level/5);
- if(level >= 55) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 56) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 57) {
- regen += 1;//GetAA(aaMentalClarity);
- }
- if(level >= 71) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 72) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 73) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 74) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- if(level >= 75) {
- regen += 1;//GetAA(aaBodyAndMindRejuvenation);
- }
- regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+ if(IsSitting())
+ Stand();
+ }
- float mana_regen_rate = RuleR(Bots, BotManaRegen);
- if(mana_regen_rate < 1.0f)
- mana_regen_rate = 1.0f;
-
- regen = regen / mana_regen_rate;
-
- SetMana(GetMana() + regen);
+ if(IsSitting()){
+ if(!rest_timer.Enabled()){
+ rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
}
}
+ else {
+ rest_timer.Disable();
+ }
}
bool Bot::BotRangedAttack(Mob* other) {
@@ -2175,6 +2543,9 @@
if(IsEngaged()) {
_ZP(Mob_BOT_Process_IsEngaged);
+ if(rest_timer.Enabled())
+ rest_timer.Disable();
+
if(IsRooted())
SetTarget(hate_list.GetClosest(this));
else
@@ -2533,6 +2904,8 @@
if(dist > GetFollowDistance()) {
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
+ if(rest_timer.Enabled())
+ rest_timer.Disable();
return;
}
else {
@@ -2842,9 +3215,6 @@
else
this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());
- // Load saved buffs
- LoadBuffs();
-
// Spawn the bot at the bow owner's loc
this->x_pos = botCharacterOwner->GetX();
this->y_pos = botCharacterOwner->GetY();
@@ -4855,7 +5225,7 @@
Group *g = GetGroup();
if(g) {
for(int i=0; i<MAX_GROUP_MEMBERS; i++) {
- if(g->members[i] && g->members[i]->IsBot() && !g->members[i]->CheckAggro(from)) {
+ if(g->members[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from)) {
g->members[i]->AddToHateList(from, 1);
}
}
@@ -5673,11 +6043,8 @@
bBlockFromRear = true;
if (damage > 0 && CanThisClassBlock() && (!other->BehindMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) {
- //skill = CastToClient()->GetSkill(BLOCKSKILL);
- /*if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(BLOCKSKILL, other, -10);
- }*/
-
+ skill = GetSkill(BLOCKSKILL);
+
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/35.0 + (GetDEX()/200);
RollTable[1] = RollTable[0] + bonus;
@@ -5687,8 +6054,8 @@
RollTable[1] = RollTable[0];
}
- if(damage > 0 && GetAA(aaShieldBlock) && (!other->BehindMob(this, other->GetX(), other->GetY()))) {
- /*bool equiped = CastToClient()->m_inv.GetItem(14);
+/* if(damage > 0 && GetAA(aaShieldBlock) && (!other->BehindMob(this, other->GetX(), other->GetY()))) {
+ bool equiped = CastToClient()->m_inv.GetItem(14);
if(equiped) {
uint8 shield = CastToClient()->m_inv.GetItem(14)->GetItem()->ItemType;
@@ -5705,19 +6072,16 @@
break;
}
}
- }*/
- }
+ }
+ }*/
//////////////////////////////////////////////////////
// parry
//////////////////////////////////////////////////////
if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY()))
{
- /*skill = CastToClient()->GetSkill(PARRY);
- if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(PARRY, other, -10);
- }*/
-
+ skill = GetSkill(PARRY);
+
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetDEX()/200);
bonus = bonus * (100 + defender->GetSpellBonuses().ParryChance + defender->GetItemBonuses().ParryChance) / 100.0f;
@@ -5734,10 +6098,7 @@
if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY()))
{
- /*skill = CastToClient()->GetSkill(DODGE);
- if (IsClient()) {
- CastToClient()->CheckIncreaseSkill(DODGE, other, -10);
- }*/
+ skill = GetSkill(DODGE);
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetAGI()/200);
@@ -5851,36 +6212,6 @@
critChance += 2;
}
- switch(GetAA(aaCombatFury))
- {
- case 1:
- critChance += 2;
- break;
- case 2:
- critChance += 4;
- break;
- case 3:
- critChance += 7;
- break;
- default:
- break;
- }
-
- switch(GetAA(aaFuryoftheAges))
- {
- case 1:
- critChance += 1;
- break;
- case 2:
- critChance += 3;
- break;
- case 3:
- critChance += 5;
- break;
- default:
- break;
- }
-
int CritBonus = GetCriticalChanceBonus(skill);
if(CritBonus > 0) {
if(critChance == 0) //If we have a bonus to crit in items or spells but no actual chance to crit
@@ -5888,25 +6219,9 @@
else
critChance += (critChance * CritBonus / 100); //crit chance is a % increase to your reg chance
}
- if(GetAA(aaSlayUndead)){
- if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){
- switch(GetAA(aaSlayUndead)){
- case 1:
- critMod += 33;
- break;
- case 2:
- critMod += 66;
- break;
- case 3:
- critMod += 100;
- break;
- }
- slayUndeadCrit = true;
- }
- }
// Paladin Bot Slay Undead AA
- if(GetClass() == PALADIN) {
+ if(GetClass() == PALADIN && GetLevel() >= 59) {
if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) {
if(GetLevel() >= 61) {
critMod += 100;
@@ -5917,6 +6232,7 @@
else if(GetLevel() >= 59) {
critMod += 33;
}
+ slayUndeadCrit = true;
}
}
@@ -5952,35 +6268,43 @@
else {
entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s scores a critical hit!(%d)", GetCleanName(), damage);
}
- /*else {
- entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s scores a critical hit!(%d)", GetCleanName(), damage);
- }*/
}
}
}
bool Bot::TryFinishingBlow(Mob *defender, SkillType skillinuse)
{
- int8 aa_item = GetAA(aaFinishingBlow) + GetAA(aaCoupdeGrace) + GetAA(aaDeathblow);
-
- if(GetLevel() >= 55) {
- aa_item += 1; // Finishing Blow AA 1
+ int8 aa_item = 0;
+ // Deathblow
+ if(GetLevel() >= 68) {
+ aa_item = 9;
}
- if(GetLevel() >= 56) {
- aa_item += 1; // Finishing Blow AA 2
+ else if(GetLevel() >= 67) {
+ aa_item = 8;
}
- if(GetLevel() >= 57) {
- aa_item += 1; // Finishing Blow AA 3
+ else if(GetLevel() >= 66) {
+ aa_item = 7;
}
- if(GetLevel() >= 62) {
- aa_item += 1; // Coup de Grace AA 1
+ // Coup de Grace AA
+ else if(GetLevel() >= 64) {
+ aa_item = 6;
}
- if(GetLevel() >= 63) {
- aa_item += 1; // Coup de Grace AA 2
+ else if(GetLevel() >= 63) {
+ aa_item = 5;
}
- if(GetLevel() >= 64) {
- aa_item += 1; // Coup de Grace AA 3
+ else if(GetLevel() >= 62) {
+ aa_item = 4;
}
+ // Finishing Blow AA
+ else if(GetLevel() >= 57) {
+ aa_item = 3;
+ }
+ else if(GetLevel() >= 56) {
+ aa_item = 2;
+ }
+ else if(GetLevel() >= 55) {
+ aa_item = 1;
+ }
if(aa_item && defender->GetHPRatio() < 10){
int chance = 0;
@@ -6155,22 +6479,6 @@
Mob* defender = this;
int totalMit = 0;
- switch(GetAA(aaCombatStability)){
- case 1:
- totalMit += 2;
- break;
- case 2:
- totalMit += 5;
- break;
- case 3:
- totalMit += 10;
- break;
- }
-
- totalMit += GetAA(aaPhysicalEnhancement)*2;
- totalMit += GetAA(aaInnateDefense);
- totalMit += GetAA(aaDefensiveInstincts)*0.5;
-
int8 botclass = GetClass();
uint8 botlevel = GetLevel();
@@ -6189,17 +6497,9 @@
}
// All Melee get Physical Enhancement AA
- if((botclass != WIZARD) &&
- (botclass != NECROMANCER) &&
- (botclass != MAGICIAN) &&
- (botclass != ENCHANTER) &&
- (botclass != DRUID) &&
- (botclass != SHAMAN))
+ if(!IsBotCaster() && botlevel >= 59)
{
- if(botlevel >= 59)
- { // Physical Enhancement AA
- totalMit += 2;
- }
+ totalMit += 2;
}
// Everyone gets Innate Defense AA
@@ -6225,9 +6525,8 @@
}
// All but pure casters get Defensive Instincts AA
- if((botclass != WIZARD) && (botclass != NECROMANCER) && (botclass != MAGICIAN) && (botclass != ENCHANTER))
+ if(!IsBotINTCaster())
{
- // Clients get this AA multiplied by a float to equal an int(totalMit)? Unfair rounding
if(botlevel >= 70) { // Defensive Instincts AA 5
totalMit += 5;
}
@@ -6438,7 +6737,7 @@
else if(GetLevel() == 65) { // Triple Backstab AA 1
tripleChance = 10;
}
- if (tripleChance > MakeRandomInt(1, 100)) {
+ if (tripleChance > MakeRandomInt(0, 99)) {
tripleBackstab = true;
}
@@ -6483,19 +6782,19 @@
}
}
}
- else if(GetAA(aaChaoticStab) > 0) {
+ // Chaotic Stab
+ else if(level >= 59) {
//we can stab from any angle, we do min damage though.
RogueBackstab(other, true);
- if (level > 54) {
- float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
- // Check for double attack with main hand assuming maxed DA Skill (MS)
- if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA
- if(other->GetHP() > 0)
- RogueBackstab(other, true);
+ float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
+ // Check for double attack with main hand assuming maxed DA Skill (MS)
+ if(MakeRandomFloat(0, 1) < DoubleAttackProbability) { // Max 62.4 % chance of DA
+ if(other->GetHP() > 0)
+ RogueBackstab(other, true);
if (tripleBackstab && other->GetHP() > 0) {
- RogueBackstab(other);
- }
+ RogueBackstab(other);
+ }
}
}
else { //We do a single regular attack if we attack from the front without chaotic stab
@@ -6665,7 +6964,7 @@
Taunt(target->CastToNPC(), true);
}*/
Say("Taunting %s", target->GetCleanName());
- Taunt(target->CastToNPC(), true);
+ Taunt(target->CastToNPC(), false);
}
if(!ca_time)
@@ -6970,6 +7269,22 @@
hasRuleDefined = true;
Result = true;
}
+ else if(attacker->IsClient() && target->IsBot()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
+ else if(attacker->IsBot() && target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = true;
+ }
+ else if(attacker->IsBot() && !target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
+ else if(attacker->IsPet() && attacker->IsFamiliar()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
else if(attacker->IsBot() && attacker->CastToBot()->GetBotOwner() && attacker->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) {
if(target->IsBot() && target->GetOwner() && target->GetOwner()->CastToClient()->GetPVP()) {
// my target is a bot and it's owner is pvp
@@ -6995,23 +7310,15 @@
Result = true;
}
}
+ else if(target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = true;
+ }
+ else if(!target->IsNPC()) {
+ hasRuleDefined = true;
+ Result = false;
+ }
}
- else if(attacker->IsClient() && target->IsBot()) {
- hasRuleDefined = true;
- Result = false;
- }
- else if(attacker->IsBot() && target->IsNPC()) {
- hasRuleDefined = true;
- Result = true;
- }
- else if(attacker->IsBot() && !target->IsNPC()) {
- hasRuleDefined = true;
- Result = false;
- }
- else if(attacker->IsPet() && attacker->IsFamiliar()) {
- hasRuleDefined = true;
- Result = false;
- }
}
return Result;
@@ -7199,49 +7506,29 @@
}
sint32 Bot::CalcMaxMana() {
- sint32 WisInt = 0;
- sint32 MindLesserFactor, MindFactor;
- switch (GetCasterClass()) {
- case 'I':
- WisInt = GetINT();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
- }
- else {
- MindLesserFactor = 0;
- }
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- max_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- max_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
- max_mana += (itembonuses.Mana + spellbonuses.Mana);
+ switch(GetCasterClass())
+ {
+ case 'I':
+ case 'W': {
+ max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana);
break;
-
- case 'W':
- WisInt = GetWIS();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
- }
- else {
- MindLesserFactor = 0;
- }
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- max_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- max_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
- max_mana += (itembonuses.Mana + spellbonuses.Mana);
+ }
+ case 'N': {
+ max_mana = 0;
break;
-
- default:
+ }
+ default: {
+ LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass());
max_mana = 0;
break;
+ }
}
+ if (cur_mana > max_mana) {
+ cur_mana = max_mana;
+ }
+ else if (max_mana < 0) {
+ max_mana = 0;
+ }
return max_mana;
}
@@ -7703,7 +7990,7 @@
}
if(IsDamageSpell(spell_id) && spells[spell_id].cast_time >= 4000) {
- if((botclass == DRUID)||(botclass == WIZARD)) {
+ if((botclass == DRUID)||(botclass == WIZARD)||(botclass == MAGICIAN)) {
if(botlevel >= 61) { // Quick Damage AA
cast_reducer += 10;
}
@@ -7850,7 +8137,6 @@
}
void Bot::DoBuffTic(int16 spell_id, int32 ticsremaining, int8 caster_level, Mob* caster) {
- if(caster && !caster->IsCorpse())
Mob::DoBuffTic(spell_id, ticsremaining, caster_level, caster);
}
@@ -8100,45 +8386,102 @@
return Result;
}
-void Bot::GenerateBaseManaPoints() {
+sint32 Bot::GenerateBaseManaPoints() {
// Now, we need to calc the base mana.
sint32 bot_mana = 0;
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+
switch (GetCasterClass()) {
case 'I':
WisInt = INT;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ if (WisInt > 100) {
+ ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
+ if (WisInt > 201) {
+ ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
+ }
+ }
+ else {
+ ConvertedWisInt = WisInt;
+ }
+ if (GetLevel() < 41) {
+ wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
+ base_mana = (GetLevel() * 15);
+ }
+ else if (GetLevel() < 81) {
+ wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
+ base_mana = (600 + ((GetLevel() - 40) * 30));
+ }
+ else {
+ wisint_mana = (9 * ConvertedWisInt);
+ base_mana = (1800 + ((GetLevel() - 80) * 18));
+ }
+ bot_mana = base_mana + wisint_mana;
}
- else {
- MindLesserFactor = 0;
+ else{
+ if((( WisInt - 199 ) / 2) > 0) {
+ MindLesserFactor = ( WisInt - 199 ) / 2;
+ }
+ else {
+ MindLesserFactor = 0;
+ }
+ MindFactor = WisInt - MindLesserFactor;
+ if(WisInt > 100) {
+ bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
+ }
+ else {
+ bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
+ }
}
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
+
bot_mana += (itembonuses.Mana + spellbonuses.Mana);
break;
case 'W':
WisInt = WIS;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ if (WisInt > 100) {
+ ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
+ if (WisInt > 201) {
+ ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
+ }
+ }
+ else {
+ ConvertedWisInt = WisInt;
+ }
+ if (GetLevel() < 41) {
+ wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
+ base_mana = (GetLevel() * 15);
+ }
+ else if (GetLevel() < 81) {
+ wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
+ base_mana = (600 + ((GetLevel() - 40) * 30));
+ }
+ else {
+ wisint_mana = (9 * ConvertedWisInt);
+ base_mana = (1800 + ((GetLevel() - 80) * 18));
+ }
+ bot_mana = base_mana + wisint_mana;
}
- else {
- MindLesserFactor = 0;
+ else{
+ if((( WisInt - 199 ) / 2) > 0) {
+ MindLesserFactor = ( WisInt - 199 ) / 2;
+ }
+ else {
+ MindLesserFactor = 0;
+ }
+ MindFactor = WisInt - MindLesserFactor;
+ if(WisInt > 100) {
+ bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
+ }
+ else {
+ bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
+ }
}
- MindFactor = WisInt - MindLesserFactor;
- if(WisInt > 100) {
- bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
- }
- else {
- bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
- }
bot_mana += (itembonuses.Mana + spellbonuses.Mana);
break;
@@ -8147,7 +8490,9 @@
break;
}
- max_mana = cur_mana = bot_mana;
+ max_mana = bot_mana;
+
+ return bot_mana;
}
void Bot::GenerateSpecialAttacks() {
@@ -8275,52 +8620,562 @@
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
CalcMaxMana();
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
}
-sint32 Bot::CalcMaxHP() {
- int16 Post255 = 0;
- sint32 bot_hp = 0;
- int16 lm = GetClassLevelFactor();
+sint32 Bot::CalcHPRegenCap(){
+ int level = GetLevel();
+ sint32 hpregen_cap = 0;
+ hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25;
+ // Energetic Attunement AAs
+ uint8 botAAlevels = level - 70;
+ if(level >= 71)
+ // This AA is capped at 5 levels
+ botAAlevels = 5;
+ hpregen_cap += botAAlevels;
- if((STA-255)/2 > 0)
- Post255 = (STA-255)/2;
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Debug, "Bot::CalcHPRegenCap() called for %s - returning %d", GetName(), hpregen_cap);
+#endif
+ return hpregen_cap;
+}
+
+sint32 Bot::CalcManaRegenCap(){
+ int level = GetLevel();
+ sint32 manaregen_cap = 0;
+ switch(GetCasterClass())
+ {
+ case 'I':
+ manaregen_cap = RuleI(Character, ItemManaRegenCap) + (itembonuses.HeroicINT / 25);
+ break;
+ case 'W':
+ manaregen_cap = RuleI(Character, ItemManaRegenCap) + (itembonuses.HeroicWIS / 25);
+ break;
+ }
+ // Expansive Mind AAs
+ uint8 botAAlevels = 0;
+ if(level >= 70) {
+ botAAlevels += 5; // To match the bonuses that clients recieve and as there are only 5 levels available to clients, bots should only recieve the same.
+ }
+ else if(level >= 66) {
+ botAAlevels += level - 65;
+ }
+ manaregen_cap += botAAlevels;
+#if EQDEBUG >= 11
+ LogFile->write(EQEMuLog::Debug, "Bot::CalcManaRegenCap() called for %s - returning %d", GetName(), manaregen_cap);
+#endif
+ return manaregen_cap;
+}
+
+// Return max stat value for level
+sint16 Bot::GetMaxStat() {
+ int level = GetLevel();
+ sint16 base = 0;
+
+ if (level < 61) {
+ base = 255;
+ }
+ else if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoF) {
+ base = 255 + 5 * (level - 60);
+ }
+ else if (level < 71) {
+ base = 255 + 5 * (level - 60);
+ }
+ else {
+ base = 330;
+ }
+
+ // Planar Power AAs, only 10 levels available to clients so bots should not receive more
+ uint8 botAAlevels = 0;
+ if(level >= 70) {
+ botAAlevels = 10;
+ }
+ else if(level >= 61) {
+ botAAlevels = level - 60;
+ }
+ base += botAAlevels * 5;
+
+ return(base);
+}
+
+sint16 Bot::GetMaxResist() {
+ int level = GetLevel();
+
+ sint16 base = 500;
+
+ if(level > 60)
+ base += ((level - 60) * 5);
+
+ // Discordant Defiance AAs, again only 5 levels for clients so only 5 for bots
+ uint8 botAAlevels = 0;
+ if(level >= 65) {
+ botAAlevels = 5;
+ }
+ else if(level >= 61) {
+ botAAlevels = level - 60;
+ }
+
+ base += botAAlevels * 5;
+ return base;
+}
+
+sint16 Bot::GetMaxSTR() {
+ return GetMaxStat()
+ + itembonuses.STRCapMod
+ + spellbonuses.STRCapMod;
+}
+sint16 Bot::GetMaxSTA() {
+ return GetMaxStat()
+ + itembonuses.STACapMod
+ + spellbonuses.STACapMod;
+}
+sint16 Bot::GetMaxDEX() {
+ return GetMaxStat()
+ + itembonuses.DEXCapMod
+ + spellbonuses.DEXCapMod;
+}
+sint16 Bot::GetMaxAGI() {
+ return GetMaxStat()
+ + itembonuses.AGICapMod
+ + spellbonuses.AGICapMod;
+}
+sint16 Bot::GetMaxINT() {
+ sint16 aaINTCapMod = 0;
+ // Innate Enlightenment AAs
+ if(this->IsBotINTCaster() && (level >= 61)) {
+ uint8 botAAlevels = 0;
+ if(level >= 65)
+ botAAlevels = 5;
+ else
+ botAAlevels = level - 60;
+ aaINTCapMod += botAAlevels * 10;
+ }
+
+ return GetMaxStat()
+ + itembonuses.INTCapMod
+ + spellbonuses.INTCapMod
+ + aaINTCapMod;
+}
+sint16 Bot::GetMaxWIS() {
+ sint16 aaWISCapMod = 0;
+ // Innate Enlightenment AAs
+ if(this->IsBotWISCaster() && (level >= 61)) {
+ uint8 botAAlevels = level - 60;
+ if(level >= 65)
+ botAAlevels = 5;
+ else
+ botAAlevels = level - 60;
+ aaWISCapMod += botAAlevels * 10;
+ }
+
+ return GetMaxStat()
+ + itembonuses.WISCapMod
+ + spellbonuses.WISCapMod
+ + aaWISCapMod;
+}
+sint16 Bot::GetMaxCHA() {
+ return GetMaxStat()
+ + itembonuses.CHACapMod
+ + spellbonuses.CHACapMod;
+}
+sint16 Bot::GetMaxMR() {
+ return GetMaxResist()
+ + itembonuses.MRCapMod
+ + spellbonuses.MRCapMod;
+}
+sint16 Bot::GetMaxPR() {
+ return GetMaxResist()
+ + itembonuses.PRCapMod
+ + spellbonuses.PRCapMod;
+}
+sint16 Bot::GetMaxDR() {
+ return GetMaxResist()
+ + itembonuses.DRCapMod
+ + spellbonuses.DRCapMod;
+}
+sint16 Bot::GetMaxCR() {
+ return GetMaxResist()
+ + itembonuses.CRCapMod
+ + spellbonuses.CRCapMod;
+}
+sint16 Bot::GetMaxFR() {
+ return GetMaxResist()
+ + itembonuses.FRCapMod
+ + spellbonuses.FRCapMod;
+}
+sint16 Bot::GetMaxCorrup() {
+ return GetMaxResist()
+ + itembonuses.CorrupCapMod
+ + spellbonuses.CorrupCapMod;
+}
+
+sint16 Bot::GetSTR() {
+ sint16 max = GetMaxSTR();
+ sint16 val = STR + itembonuses.STR + spellbonuses.STR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetSTA() {
+ sint16 max = GetMaxSTA();
+ sint16 val = STA + itembonuses.STA + spellbonuses.STA;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetDEX() {
+ sint16 max = GetMaxDEX();
+ sint16 val = DEX + itembonuses.DEX + spellbonuses.DEX;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetAGI() {
+ sint16 max = GetMaxAGI();
+ sint16 val = AGI + itembonuses.AGI + spellbonuses.AGI;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetINT() {
+ sint16 max = GetMaxINT();
+ sint16 val = INT + itembonuses.INT + spellbonuses.INT;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetWIS() {
+ sint16 max = GetMaxWIS();
+ sint16 val = WIS + itembonuses.WIS + spellbonuses.WIS;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCHA() {
+ sint16 max = GetMaxCHA();
+ sint16 val = CHA + itembonuses.CHA + spellbonuses.CHA;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetMR() {
+ sint16 max = GetMaxMR();
+ sint16 val = MR + itembonuses.MR + spellbonuses.MR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetFR() {
+ sint16 max = GetMaxFR();
+ sint16 val = FR + itembonuses.FR + spellbonuses.FR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetDR() {
+ sint16 max = GetMaxDR();
+ sint16 val = DR + itembonuses.DR + spellbonuses.DR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetPR() {
+ sint16 max = GetMaxPR();
+ sint16 val = PR + itembonuses.PR + spellbonuses.PR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCR() {
+ sint16 max = GetMaxCR();
+ sint16 val = CR + itembonuses.CR + spellbonuses.CR;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+sint16 Bot::GetCorrup() {
+ sint16 max = GetMaxCorrup();
+ sint16 val = Corrup + itembonuses.Corrup + spellbonuses.Corrup;
+
+ if(val > max)
+ val = max;
+
+ return val;
+}
+
+void Bot::CalcRestState() {
+
+ // This method calculates rest state HP and mana regeneration.
+ // The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
+ // must be sitting down, and must not have any detrimental spells affecting them.
+ //
+ if(!RuleI(Character, RestRegenPercent))
+ return;
+
+ RestRegenHP = RestRegenMana = 0;
+
+ if(IsEngaged() || !IsSitting())
+ return;
+
+ if(!rest_timer.Check(false))
+ return;
+
+ uint32 buff_count = GetMaxTotalSlots();
+ for (unsigned int j = 0; j < buff_count; j++) {
+ if(buffs[j].spellid != SPELL_UNKNOWN) {
+ if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0))
+ if(!DetrimentalSpellAllowsRest(buffs[j].spellid))
+ return;
+ }
+ }
+
+ RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
+
+ RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
+}
+
+sint32 Bot::LevelRegen()
+{
+ int level = GetLevel();
+ bool bonus = GetRaceBitmask(_baseRace) & RuleI(Character, BaseHPRegenBonusRaces);
+ uint8 multiplier1 = bonus ? 2 : 1;
+ sint32 hp = 0;
+
+ //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities
+ if (level < 51) {
+ if (IsSitting()) {
+ if (level < 20)
+ hp += 2 * multiplier1;
+ else if (level < 50)
+ hp += 3 * multiplier1;
+ else //level == 50
+ hp += 4 * multiplier1;
+ }
+ else //feigned or standing
+ hp += 1 * multiplier1;
+ }
+ //there may be an easier way to calculate this next part, but I don't know what it is
+ else { //level >= 51
+ sint32 tmp = 0;
+ float multiplier2 = 1;
+ if (level < 56) {
+ tmp = 2;
+ if (bonus)
+ multiplier2 = 3;
+ }
+ else if (level < 60) {
+ tmp = 3;
+ if (bonus)
+ multiplier2 = 3.34;
+ }
+ else if (level < 61) {
+ tmp = 4;
+ if (bonus)
+ multiplier2 = 3;
+ }
+ else if (level < 63) {
+ tmp = 5;
+ if (bonus)
+ multiplier2 = 2.8;
+ }
+ else if (level < 65) {
+ tmp = 6;
+ if (bonus)
+ multiplier2 = 2.67;
+ }
+ else { //level >= 65
+ tmp = 7;
+ if (bonus)
+ multiplier2 = 2.58;
+ }
+
+ hp += sint32(float(tmp) * multiplier2);
+ }
+
+ return hp;
+}
+
+sint32 Bot::CalcHPRegen() {
+ sint32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen + (itembonuses.HeroicSTA / 25);
+ // Innate Regeneration AA
+ uint8 botAAlevels = 0;
+ if(level >= 51) {
+ if(level >= 53)
+ botAAlevels += 3;
+ else
+ botAAlevels += level - 50;
+ }
+ // Natural Healing AA
+ if(level >= 56) {
+ if(level >= 58)
+ botAAlevels += 3;
+ else
+ botAAlevels += level - 55;
+ }
+ // Body and Mind Rejuvenation AA
+ if(level >= 59)
+ botAAlevels += 1;
+
+ // Convalescence AA
+ if(level >= 61) {
+ if(level >= 62)
+ botAAlevels += 2;
+ else
+ botAAlevels += level - 60;
+ }
+ // Healthy Aura AA
+ if(level >= 66) {
+ if(level >= 70)
+ botAAlevels += 5;
+ else
+ botAAlevels += level - 65;
+ }
+
+ regen = ((regen + botAAlevels) * RuleI(Character, HPRegenMultiplier)) / 100;
+ return regen;
+}
+
+sint32 Bot::CalcManaRegen()
+{
+ uint8 level = GetLevel();
+ uint8 botclass = GetClass();
+ sint32 regen = 0;
+ //this should be changed so we dont med while camping, etc...
+ if (IsSitting())
+ {
+ BuffFadeBySitModifier();
+ if(botclass != WARRIOR && botclass != MONK && botclass != ROGUE && botclass != BERSERKER) {
+ regen = (((GetSkill(MEDITATE) / 10) + (level - (level / 4))) / 4) + 4;
+ regen += spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+ else
+ regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+ else {
+ regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
+ }
+
+ uint8 botAAlevels = 0;
+ if(level >= 56) {
+ if(level >= 58)
+ botAAlevels = 3;
+ else
+ botAAlevels = level - 55;
+ }
+
+ regen += botAAlevels;
+
+ if(GetCasterClass() == 'I')
+ regen += (itembonuses.HeroicINT / 25);
+ else if(GetCasterClass() == 'W')
+ regen += (itembonuses.HeroicWIS / 25);
else
- Post255 = 0;
+ regen = 0;
- bot_hp = (5)+(GetLevel()*lm/10) + (((STA-Post255)*GetLevel()*lm/3000));
- bot_hp += itembonuses.HP;
+ regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+
+ float mana_regen_rate = RuleR(Bots, BotManaRegen);
+ if(mana_regen_rate < 0.0f)
+ mana_regen_rate = 0.0f;
+
+ regen = regen * mana_regen_rate; // 90% of people wouldnt guess that manaregen would decrease the larger the number they input, this makes more sense
+
+ return regen;
+}
+
+// This is for calculating Base HPs + STA bonus for SoD or later clients.
+int32 Bot::GetClassHPFactor() {
+
+ int factor;
+
+ // Note: Base HP factor under level 41 is equal to factor / 12, and from level 41 to 80 is factor / 6.
+ // Base HP over level 80 is factor / 10
+ // HP per STA point per level is factor / 30 for level 80+
+ // HP per STA under level 40 is the level 80 HP Per STA / 120, and for over 40 it is / 60.
+ switch(GetClass())
+ {
+ case DRUID:
+ case ENCHANTER:
+ case NECROMANCER:
+ case MAGICIAN:
+ case WIZARD:
+ factor = 240;
+ break;
+ case BEASTLORD:
+ case BERSERKER:
+ case MONK:
+ case ROGUE:
+ case SHAMAN:
+ factor = 255;
+ break;
+ case BARD:
+ case CLERIC:
+ factor = 264;
+ break;
+ case SHADOWKNIGHT:
+ case PALADIN:
+ factor = 288;
+ break;
+ case RANGER:
+ factor = 276;
+ break;
+ case WARRIOR:
+ factor = 300;
+ break;
+ default:
+ factor = 240;
+ break;
+ }
+ return factor;
+}
+
+sint32 Bot::CalcMaxHP() {
+ sint32 bot_hp = 0;
+
+ bot_hp += GenerateBaseHitPoints() + itembonuses.HP;
+
// Hitpoint AA's
int32 nd = 10000;
if(GetLevel() >= 69) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 5
- nd += 500;
- }
}
else if(GetLevel() >= 68) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 4
- nd += 400;
- }
}
else if(GetLevel() >= 67) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 3
- nd += 300;
- }
}
else if(GetLevel() >= 66) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 2
- nd += 200;
- }
}
else if(GetLevel() >= 65) {
nd += 1650; // Planar Durablility AA 3
- if(GetClass() == WARRIOR) { // Sturdiness AA 1
- nd += 100;
- }
}
else if(GetLevel() >= 63) {
nd += 1500; // Planar Durablility AA 2
@@ -8343,8 +9198,19 @@
bot_hp = bot_hp * nd / 10000;
+ if(GetClass() == WARRIOR) { // Sturdiness AAs
+ if(GetLevel() >= 65){
+ uint8 botAAlevels = GetLevel() - 64;
+ if(botAAlevels > 5)
+ botAAlevels = 5;
+ bot_hp += botAAlevels * 100;
+ }
+ }
+
bot_hp += spellbonuses.HP;
+ bot_hp += bot_hp * (spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000;
+
max_hp = bot_hp;
return max_hp;
@@ -8694,59 +9560,7 @@
}
}
-// This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc.
-void Bot::CalcBotStats(bool showtext) {
- if(!GetBotOwner())
- return;
-
- if(showtext) {
- GetBotOwner()->Message(15, "Bot updating...");
- }
-
- if(!IsValidRaceClassCombo()) {
- GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel()));
- GetBotOwner()->Message(15, "Previous Bots Code releases did not check Race/Class combinations during create.");
- GetBotOwner()->Message(15, "Unless you are experiencing heavy lag, you should delete and remake this bot.");
- }
-
- if(GetBotOwner()->GetLevel() != GetLevel())
- SetLevel(GetBotOwner()->GetLevel());
-
- GenerateBaseStats();
-
- GenerateAABonuses();
-
- GenerateArmorClass();
-
- //// Calc Base Hit Points
- //int16 lm = GetClassLevelFactor();
- //int16 Post255;
- //if((bsta-255)/2 > 0)
- // Post255 = (bsta-255)/2;
- //else
- // Post255 = 0;
- //sint32 bot_hp = (5)+(blevel*lm/10) + (((bsta-Post255)*blevel*lm/3000)) + ((Post255*blevel)*lm/6000);
- GenerateBaseHitPoints();
-
- GenerateBaseManaPoints();
-
- GenerateSpecialAttacks();
-
- if(showtext) {
- GetBotOwner()->Message(15, "Base stats:");
- GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), base_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
- GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
- }
-
- // Let's find the items in the bot inventory
- sint32 items_hp = 0;
- sint32 items_mana = 0;
-
- /*if(this->Save())
- this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName());
- else
- this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());*/
-
+void Bot::CalcItemBonuses() {
memset(&itembonuses, 0, sizeof(StatBonuses));
const Item_Struct* itemtmp = 0;
@@ -8887,47 +9701,66 @@
}
}
- MR += itembonuses.MR;
- CR += itembonuses.CR;
- DR += itembonuses.DR;
- FR += itembonuses.FR;
- PR += itembonuses.PR;
- Corrup += itembonuses.Corrup;
- AC += itembonuses.AC;
- STR += itembonuses.STR;
- STA += itembonuses.STA;
- DEX += itembonuses.DEX;
- AGI += itembonuses.AGI;
- INT += itembonuses.INT;
- WIS += itembonuses.WIS;
- CHA += itembonuses.CHA;
- ATK += itembonuses.ATK;
+ if(itembonuses.HPRegen > CalcHPRegenCap())
+ itembonuses.HPRegen = CalcHPRegenCap();
- MR += spellbonuses.MR;
- CR += spellbonuses.CR;
- DR += spellbonuses.DR;
- FR += spellbonuses.FR;
- PR += spellbonuses.PR;
- Corrup += spellbonuses.Corrup;
- AC += spellbonuses.AC;
- STR += spellbonuses.STR;
- STA += spellbonuses.STA;
- DEX += spellbonuses.DEX;
- AGI += spellbonuses.AGI;
- INT += spellbonuses.INT;
- WIS += spellbonuses.WIS;
- CHA += spellbonuses.CHA;
- ATK += spellbonuses.ATK;
+ if(itembonuses.ManaRegen > CalcManaRegenCap())
+ itembonuses.ManaRegen = CalcManaRegenCap();
+}
- cur_hp = CalcMaxHP();
- GenerateBaseManaPoints();
+// This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc.
+void Bot::CalcBotStats(bool showtext) {
+ if(!GetBotOwner())
+ return;
+
+ if(showtext) {
+ GetBotOwner()->Message(15, "Bot updating...");
+ }
+ if(!IsValidRaceClassCombo()) {
+ GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel()));
+ GetBotOwner()->Message(15, "Previous Bots Code releases did not check Race/Class combinations during create.");
+ GetBotOwner()->Message(15, "Unless you are experiencing heavy lag, you should delete and remake this bot.");
+ }
+
+ if(GetBotOwner()->GetLevel() != GetLevel())
+ SetLevel(GetBotOwner()->GetLevel());
+
+ GenerateBaseStats();
+
+ GenerateAABonuses();
+
+ GenerateSpecialAttacks();
+
+ if(showtext) {
+ GetBotOwner()->Message(15, "Base stats:");
+ GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), base_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
+ GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
+ }
+
+ // Let's find the items in the bot inventory
+
+ /*if(this->Save())
+ this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName());
+ else
+ this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());*/
+
+ CalcItemBonuses();
+ CalcSpellBonuses(&spellbonuses);
+ GenerateArmorClass();
+
+ CalcMaxHP();
+ CalcMaxMana();
+
+ hp_regen = CalcHPRegen();
+ mana_regen = CalcManaRegen();
+
AI_AddNPCSpells(this->GetBotSpellID());
if(showtext) {
GetBotOwner()->Message(15, "I'm updated.");
- GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), max_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA);
- GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup);
+ GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), max_hp, GetAC(), max_mana, GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA());
+ GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",GetMR(),GetPR(),GetFR(),GetCR(),GetDR(),GetCorrup ());
}
}
botspellsai.cpp
Index: botspellsai.cpp
================================================== =================
--- botspellsai.cpp (revision 1832)
+++ botspellsai.cpp (working copy)
@@ -209,8 +209,7 @@
break;
}
case SpellType_Nuke: {
- if(((MakeRandomInt(1, 100) <= iChance) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
- && ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER))))
+ if((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
{
if(!checked_los) {
if(!CheckLosFN(tar))
@@ -228,7 +227,17 @@
else if(botClass == WIZARD) {
botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar);
}
-
+
+ if(botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER) {
+ if(botSpell.SpellId == 0){
+ int8 stunChance = (tar->IsCasting() ? 30: 15);
+
+ if(!tar->IsStunned() && ( botClass == PALADIN || (MakeRandomInt(1, 100) <= stunChance))){
+ botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target);
+ }
+ }
+ }
+
if(botSpell.SpellId == 0)
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target);
@@ -1207,7 +1214,7 @@
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
// Assuming all the spells have been loaded into this list by level and in descending order
- if(IsPureNukeSpell(botSpellListItr->SpellId)) {
+ if(IsPureNukeSpell(botSpellListItr->SpellId) && IsDamageSpell(botSpellListItr->SpellId)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -1220,6 +1227,30 @@
return result;
}
+BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) {
+ BotSpell result;
+
+ result.SpellId = 0;
+ result.SpellIndex = 0;
+ result.ManaCost = 0;
+
+ if(botCaster) {
+ std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType);
+
+ for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
+ // Assuming all the spells have been loaded into this list by level and in descending order
+ if(IsStunSpell(botSpellListItr->SpellId)) {
+ result.SpellId = botSpellListItr->SpellId;
+ result.SpellIndex = botSpellListItr->SpellIndex;
+ result.ManaCost = botSpellListItr->ManaCost;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) {
BotSpell result;