bad_captain
11-22-2010, 12:24 PM
I have combined my earlier submissions, and added a few others changes. These diffs are against Rev 1742, so it does include Caryatis' bot changes in Rev 1741 for silence not affecting disciplines. Here are the changes:
1- Added SoD HP/Mana calculations for bots of clients who use SoD+
2- Changed AC calculations to match client calcs
3- Changed ATK calculations to match client calcs
4- Added stat caps and removed double counting of stats from items & spells
5- Fixed HP regen
6- Fixed Mana regen
7- Fixed buffs bugs
8- Added #bot showstats comand that uses the new ShowStats window (thanks Caryatis)
9- Fixed Paladin bot ATK bug
10- Fixed item links for inventory list command for SoD+ clients
Recommended SQL
UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen';
bot.h
--- EQEmuServer/zone/bot.h (rev 1742) Mon Nov 22 00:41:04 2010
+++ C:/EqEmuSource/EQEmuServer/zone/bot.h Sat Nov 20 22:45:03 2010
@@ -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,28 @@
bool IsStanding();
bool IsBotCasterCombatRange(Mob *target);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ;
+ int16 MaxSkill(SkillType skillid, int16 class_, int16 level) const;
+ 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();
+ virtual void ShowStats(Client* client);
// AI Methods
virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
@@ -263,7 +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); }
+ 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);
virtual void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; }
@@ -336,6 +411,7 @@
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();
bot.cpp
--- EQEmuServer/zone/bot.cpp (Rev 1742) Mon Nov 22 11:08:27 2010
+++ C:/EqEmulator/EQEmuServer/zone/bot.cpp Mon Nov 22 11:02:38 2010
@@ -120,11 +120,13 @@
}
GenerateBaseStats();
- GenerateArmorClass();
// Calculate HitPoints Last As It Uses Base Stats
GenerateBaseHitPoints();
+ // Load saved buffs
+ LoadBuffs();
+
CalcBotStats(false);
}
@@ -409,7 +411,7 @@
Wisdom += 15;
Charisma += 10;
Dexterity += 5;
- Attack =+ 17;
+ Attack += 17;
DiseaseResist += 8;
break;
case 4: // Ranger
@@ -763,19 +765,491 @@
}
+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;
}
+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(13);
+
+ if (!equiped)
+ skill = HAND_TO_HAND;
+
+ else {
+
+ uint8 type = m_inv.GetItem(13)->GetItem()->ItemType; //is this the best way to do this?
+
+ switch (type)
+ {
+ case ItemType1HS: // 1H Slashing
+ {
+ skill = _1H_SLASHING;
+ break;
+ }
+ case ItemType2HS: // 2H Slashing
+ {
+ skill = _2H_SLASHING;
+ break;
+ }
+ case ItemTypePierce: // Piercing
+ {
+ skill = PIERCING;
+ break;
+ }
+ case ItemType1HB: // 1H Blunt
+ {
+ skill = _1H_BLUNT;
+ break;
+ }
+ case ItemType2HB: // 2H Blunt
+ {
+ skill = _2H_BLUNT;
+ break;
+ }
+ 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;
+ }
+ }
+ }
+
+ return GetSkill(skill);
+}
+
+sint16 Bot::GetATK() {
+ int16 AttackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ if(WornCap > 250)
+ WornCap = 250;
+
+ AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69));
+
+ if (AttackRating < 10)
+ AttackRating = 10;
+
+ return AttackRating;
+}
+
void Bot::GenerateBaseHitPoints() {
// Calc Base Hit Points
/*int16 multiplier = 1;
@@ -810,21 +1284,52 @@
break;
}
int16 lm = multiplier;*/
+ 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;
+ Client* c = this->GetOwner()->CastToClient();
+ if(c && c->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;
}
void Bot::GenerateAABonuses() {
+ memset(&aabonuses, 0, sizeof(StatBonuses));
// General AA bonus
uint8 botlevel = GetLevel();
if(botlevel >= 51) {
@@ -1696,10 +2201,10 @@
// SetHP(GetHP()+hp_regen);
if(GetHP() < GetMaxHP())
- SetHP(GetHP() + hp_regen + bonus);
+ SetHP(GetHP() + CalcHPRegen() + bonus);
if(GetMana() < GetMaxMana())
- SetMana(GetMana() + mana_regen + bonus);
+ SetMana(GetMana() + CalcManaRegen() + bonus);
}
if (sendhpupdate_timer.Check()) {
@@ -1731,127 +2236,20 @@
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();
+ //SetAppearance(eaSitting, false);
}
else {
- SetAppearance(eaStanding, false);
+ if(IsSitting())
+ Stand();
+ //SetAppearance(eaStanding, false);
}
}
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;
-
- 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())
+ Stand();
+ //SetAppearance(eaStanding, false);
}
}
@@ -2842,9 +3240,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();
@@ -7122,41 +7517,100 @@
sint32 Bot::CalcMaxMana() {
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+ Client* c = this->GetOwner()->CastToClient();
switch (GetCasterClass()) {
case 'I':
WisInt = GetINT();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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));
+ }
+ max_mana = base_mana + wisint_mana;
+ max_mana += (itembonuses.Mana + spellbonuses.Mana);
}
- else {
- MindLesserFactor = 0;
+ else{
+ 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);
}
- 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);
break;
case 'W':
WisInt = GetWIS();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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));
+ }
+ max_mana = base_mana + wisint_mana;
+ max_mana += (itembonuses.Mana + spellbonuses.Mana);
}
- else {
- MindLesserFactor = 0;
+ else{
+ 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);
}
- 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);
break;
default:
@@ -7771,8 +8225,7 @@
}
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);
+ Mob::DoBuffTic(spell_id, ticsremaining, caster_level, caster);
}
bool Bot::CastSpell(int16 spell_id, int16 target_id, int16 slot, sint32 cast_time, sint32 mana_cost, int32* oSpellWillFinish, int32 item_slot) {
@@ -8026,40 +8479,98 @@
sint32 bot_mana = 0;
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+ Client* c = this->GetOwner()->CastToClient();
+
switch (GetCasterClass()) {
case 'I':
WisInt = INT;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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 (c && c->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;
@@ -8195,20 +8706,535 @@
void Bot::CalcBonuses() {
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
- CalcMaxMana();
}
+sint32 Bot::CalcHPRegenCap(){
+ int level = GetLevel();
+ sint32 hpregen_cap = 0;
+ hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25;
+ if(level >= 71) {
+ //level 71 = 1 AA level
+ uint8 botAAlevels = level - 70;
+ hpregen_cap += botAAlevels * 1; // Energetic Attunement AAs
+ }
+#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;
+ }
+ if(level >= 85) {
+ uint8 botAAlevels = 25;
+ manaregen_cap += botAAlevels * 1; // Expansive Mind AAs
+ }
+ else if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ manaregen_cap += botAAlevels * 1; // Expansive Mind AAs
+ }
+#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();
+ Client* c = this->GetOwner()->CastToClient();
+
+ sint16 base = 0;
+
+ if (level < 61) {
+ base = 255;
+ }
+ else if (c && c->GetClientVersion() >= EQClientSoF) {
+ base = 255 + 5 * (level - 60);
+ }
+ else if (level < 71) {
+ base = 255 + 5 * (level - 60);
+ }
+ else {
+ base = 330;
+ }
+
+ if(level >= 81) {
+ uint8 botAAlevels = 20;
+ base += botAAlevels * 5; // Planar Power AAs
+ }
+ else if(level >= 61) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ base += botAAlevels * 5; // Planar Power AAs
+ }
+
+ return(base);
+}
+
+sint16 Bot::GetMaxResist() {
+ int level = GetLevel();
+
+ sint16 base = 500;
+
+ if(level > 60)
+ base += ((level - 60) * 5);
+
+ if(level >= 80) {
+ uint8 botAAlevels = 15;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+ else if(level >= 76) {
+ uint8 botAAlevels = 10;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+ else if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+
+ 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;
+ if(this->IsBotCaster() && (level >= 61)) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ aaINTCapMod += botAAlevels * 10; // Innate Enlightenment AAs
+ }
+
+ return GetMaxStat()
+ + itembonuses.INTCapMod
+ + spellbonuses.INTCapMod
+ + aaINTCapMod;
+}
+sint16 Bot::GetMaxWIS() {
+ sint16 aaWISCapMod = 0;
+ if(this->IsBotCaster() && (level >= 61)) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ aaWISCapMod += botAAlevels * 10; // Innate Enlightenment AAs
+ }
+
+ 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;
+}
+
+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);
+ //AAs
+ if(level >= 51) {
+ uint8 botAAlevels = 3;
+ regen += botAAlevels * 1; // Innate Regeneration AAs
+ }
+
+ if(level >= 61) {
+ uint8 botAAlevels = 2;
+ regen += botAAlevels * 1; // Convalescence AAs
+ }
+
+ if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ if(botAAlevels > 15)
+ botAAlevels = 15;
+ regen += botAAlevels * 1; // Healthy Aura, Natural Healing, Body and Mind Rejuvenation AAs
+ }
+
+ regen = (regen * 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;
+ }
+
+ //AAs
+ if(level >= 55) {
+ uint8 botAAlevels = 3;
+ regen += botAAlevels * 1; // Mental Clarity AAs
+ }
+
+ if(level >= 85) {
+ uint8 botAAlevels = 23;
+ regen += botAAlevels * 1; // TSS, SoF, SoD, Underfoot AAs
+ }
+ else if(level >= 71) {
+ uint8 botAAlevels = level - 70;
+ regen += botAAlevels * 1; // TSS, SoF, SoD AAs
+ }
+
+ if(GetCasterClass() == 'I')
+ regen += (itembonuses.HeroicINT / 25);
+ else if(GetCasterClass() == 'W')
+ regen += (itembonuses.HeroicWIS / 25);
+ else
+ regen = 0;
+
+ regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+
+ float mana_regen_rate = RuleR(Bots, BotManaRegen);
+ if(mana_regen_rate <= 0.0f)
+ mana_regen_rate = 1.0f;
+
+ regen = regen / mana_regen_rate;
+
+ 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() {
- int16 Post255 = 0;
sint32 bot_hp = 0;
- int16 lm = GetClassLevelFactor();
+ Client* c = this->GetOwner()->CastToClient();
+ if(c && c->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ float SoDPost255;
+ int16 NormalSTA = GetSTA();
- if((STA-255)/2 > 0)
- Post255 = (STA-255)/2;
- else
- Post255 = 0;
+ if(((NormalSTA - 255) / 2) > 0)
+ SoDPost255 = ((NormalSTA - 255) / 2);
+ else
+ SoDPost255 = 0;
- bot_hp = (5)+(GetLevel()*lm/10) + (((STA-Post255)*GetLevel()*lm/3000));
+ int hp_factor = GetClassHPFactor();
+
+ if (level < 41) {
+ bot_hp = (5 + (GetLevel() * hp_factor / 12) +
+ ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600));
+ }
+ else if (level < 81) {
+ bot_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800));
+ }
+ else {
+ bot_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * hp_factor / 45));
+ }
+ }
+ else{
+ int16 Post255 = 0;
+ int16 NormalSTA = GetSTA();
+ int16 lm = GetClassLevelFactor();
+
+ if((NormalSTA-255)/2 > 0)
+ Post255 = (NormalSTA-255)/2;
+ else
+ Post255 = 0;
+
+ bot_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*GetLevel())*lm/6000);
+ }
bot_hp += itembonuses.HP;
// Hitpoint AA's
@@ -8615,59 +9641,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;
@@ -8808,47 +9782,76 @@
}
}
- 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();
+}
+// 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();
+
+ //// 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
+
+ /*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();
cur_hp = CalcMaxHP();
- GenerateBaseManaPoints();
+ cur_mana = CalcMaxMana();
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 ());
}
}
@@ -8932,6 +9935,7 @@
c->Message(0, "#bot botgroup help - Displays the commands available to manage BOT ONLY groups.");
c->Message(0, "#bot mana [<bot name or target> | all] - Displays a mana report for all your spawned bots.");
c->Message(0, "#bot [hair|haircolor|beard|beardcolor|face|eyes|heritage |tattoo|details <value>] - Change your BOTs appearance.");
+ c->Message(0, "#bot showstats - Show details about your bot.");
// TODO:
// c->Message(0, "#bot illusion <bot/client name or target> - Enchanter Bot cast an illusion buff spell on you or your target.");
return;
@@ -9375,7 +10379,7 @@
char* itemLink = 0;
if((i == 0) || (i == 11) || (i == 13) || (i == 14) || (i == 21)) {
- if (c->GetClientVersion() == EQClientSoF)
+ if (c->GetClientVersion() >= EQClientSoF)
{
MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X",
0,
@@ -9411,7 +10415,7 @@
}
}
else {
- if (c->GetClientVersion() == EQClientSoF)
+ if (c->GetClientVersion() >= EQClientSoF)
{
MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X",
0,
@@ -12024,6 +13028,22 @@
}
return;
}
+
+ if(!strcasecmp(sep->arg[1], "showstats")) {
+ if (c->GetTarget() == 0)
+ c->Message(0, "Error: no target");
+ else if (!c->GetTarget()->IsBot()) {
+ c->Message(0, "Error: you must target a bot");
+ }
+ else{
+ Bot *b = c->GetTarget()->CastToBot();
+
+ if(b){
+ b->ShowStats(c);
+ }
+ }
+ return;
+ }
}
// franck: EQoffline
@@ -12428,4 +13448,94 @@
return;
}
+void Bot::ShowStats(Client* client) {
+
+ int16 attackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ attackRating = GetATK();
+
+ if(client && RuleB(Character, UseNewStatsWindow)) {
+
+ // Define the types of page breaks we need
+ const char indP[] = " ";
+ const char indS[] = " ";
+ const char indM[] = " ";
+ const char indL[] = " ";
+ const char div[] = " | ";
+
+ client->SendStatWindow(" "
+ /* 01 - Name */ " %s <c \"#357EC7\"> %s %s</c><br>"
+ /* 02 - L/C/R */ " %s Class: %i %s Level: %i %s %s Race: %i<br>"
+ /* 03 - AC/Atk */ " %s ATK: %i<br>"
+ /* 04 - Labels */ " %s AC: %i %s %s %s Items Spells AAs Total Cap<br>"
+ /* 05 - HP */ " %s HP: %s %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 06 - Mana */ " %s Mana: %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 07 - End. */ " %s End.: %s %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 08 - LineBr */ " <br>"
+ /* 09 - STR/MR */ " %s STR: %i | + %i %s MR: %i | + %i<br>"
+ /* 10 - STA/CR */ " %s STA: %i | + %i %s CR: %i | + %i<br>"
+ /* 11 - AGI/FR */ " %s AGI: %i | + %i %s FR: %i | + %i<br>"
+ /* 12 - DEX/PR */ " %s DEX: %i | + %i %s PR: %i | + %i<br>"
+ /* 13 - INT/DR */ " %s INT: %i | + %i %s DR: %i | + %i<br>"
+ /* 14 - WIS/Cp */ " %s WIS: %i | + %i %s Cp: %i | + %i<br>"
+ /* 15 - CHA/Ph */ " %s CHA: %i | + %i %s Physical: %i<br>"
+ /* 16 - LineBr */ " <br>"
+ /* 17 - Avd/Hat */ " %s Avoidance: %i / %i %s Heal Amt.: %i / %i<br>"
+ /* 18 - Acc/Sdg */ " %s Accuracy: %i / %i %s Spell Dmg.: %i / %i<br>"
+ /* 19 - CE/Clar */ " %s Combat Effects: %i / %i %s Clair.: %i / %i<br>"
+ /* 20 - DSd/DSm */ " %s DoT Shield.: %i / %i %s DS Mit.: %i / %i<br>"
+ /* 21 - Shield */ " %s Shielding: %i / %i<br>"
+ /* 22 - SS */ " %s Spell Shield.: %i / %i<br>"
+ /* 23 - Strike */ " %s Strike.: %i / %i<br>"
+ /* 24 - SR */ " %s Stun Resist: %i / %i" ,
+ /* 01 - Name */ indL, GetName(), GetLastName(),
+ /* 02 - L/C/R */ indP, GetClass(), indM, GetLevel(), indS, indP, GetBaseRace(),
+ /* 03 - AC/Atk */ indP, GetATK(),
+ /* 04 - Labels */ indP, GetAC(), indS, indP, indP,
+ /* 05 - HP */ indP, indP, GetMaxHP(), indS, itembonuses.HPRegen, div, spellbonuses.HPRegen, div, aabonuses.HPRegen, div, CalcHPRegen(), div, CalcHPRegenCap(),
+ /* 06 - Mana */ indP, GetMaxMana(), indS, itembonuses.ManaRegen, div, spellbonuses.ManaRegen, div, aabonuses.ManaRegen, div, CalcManaRegen(), div, CalcManaRegenCap(),
+ /* 07 - End. */ indP, indP, 0, indS, 0, div, 0, div, 0, div, 0, div, 0,
+ /* 08 - LineBr */
+ /* 09 - STR/MR */ indP, GetSTR(), GetHeroicSTR(), indL, GetMR(), GetHeroicMR(),
+ /* 10 - STA/CR */ indP, GetSTA(), GetHeroicSTA(), indL, GetCR(), GetHeroicCR(),
+ /* 11 - AGI/FR */ indP, GetAGI(), GetHeroicAGI(), indL, GetFR(), GetHeroicFR(),
+ /* 12 - DEX/PR */ indP, GetDEX(), GetHeroicDEX(), indL, GetPR(), GetHeroicPR(),
+ /* 13 - INT/DR */ indP, GetINT(), GetHeroicINT(), indL, GetDR(), GetHeroicDR(),
+ /* 14 - WIS/Cp */ indP, GetWIS(), GetHeroicWIS(), indL, GetCorrup(), GetHeroicCorrup(),
+ /* 15 - CHA/Ph */ indP, GetCHA(), GetHeroicCHA(), indL, ((GetSTR() + GetSTA()) / 2),
+ /* 16 - LineBr */
+ /* 17 - Avd/Hat */ indP, GetAvoidance(), RuleI(Character, ItemAvoidanceCap), indS, GetHealAmt(), RuleI(Character, ItemHealAmtCap),
+ /* 18 - Acc/Sdg */ indP, GetAccuracy(), RuleI(Character, ItemAccuracyCap), indS, GetSpellDmg(), RuleI(Character, ItemSpellDmgCap),
+ /* 19 - CE/Clar */ indP, GetCombatEffects(), RuleI(Character, ItemCombatEffectsCap), indS, GetClair(), RuleI(Character, ItemClairvoyanceCap),
+ /* 20 - DSd/DSm */ indP, GetDoTShield(), RuleI(Character, ItemDoTShieldingCap), indM, GetDSMit(), RuleI(Character, ItemDSMitigationCap),
+ /* 21 - Shield */ indP, GetShielding(), RuleI(Character, ItemShieldingCap),
+ /* 22 - SS */ indP, GetSpellShield(), RuleI(Character, ItemSpellShieldingCap),
+ /* 23 - Strike */ indP, GetStrikeThrough(), RuleI(Character, ItemStrikethroughCap),
+ /* 24 - SR */ indP, GetStunResist(), RuleI(Character, ItemStunResistCap)
+ );
+ }
+ else {
+
+ client->Message(0, "Name: %s %s", GetName(), lastname);
+
+ client->Message(0, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), GetAC(), GetClass(), GetSize(), GetHaste());
+ client->Message(0, " HP: %i Max HP: %i HP Regen: %i", GetHP(), GetMaxHP(), CalcHPRegen());
+ client->Message(0, " Mana: %i Max Mana: %i Mana Regen: %i", GetMana(), GetMaxMana(), CalcManaRegen());
+ client->Message(0, " Total ATK: %i Worn/Spell ATK (Cap 250): %i Server Used ATK: %i", attackRating, GetATKBonus(), attackRating);
+ client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA());
+ client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR());
+ client->Message(0, " Race: %i BaseRace: %i Texture: %i HelmTexture: %i Gender: %i BaseGender: %i", GetRace(), GetBaseRace(), GetTexture(), GetHelmTexture(), GetGender(), GetBaseGender());
+
+ }
+
+ if (client->Admin() >= 100) {
+ client->Message(0, " EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i",
+ GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted);
+ if (this->IsAIControlled()) {
+ client->Message(0, " AIControlled: AggroRange: %1.0f AssistRange: %1.0f", GetAggroRange(), GetAssistRange());
+ }
+ }
+}
+
#endif
Changes explanation:
1- Bots owned by character who uses SoD+ will use the new HP/Mana calculations, adding a significant amount of HPs and Mana to the bots.
2- Changed the AC calculations to match the client calculations, adding ~400 AC to lvl 65 Warrior (from 1100 - 1500). The previous calculation used mob's GetAC which only adds item AC (bot double counted), spell AC, and the bot's base AC (which starts as 12) and multiplied it by 1.5 if the bot was a Warrior or Knight.
3- Changed the ATK calculations to match the client calculations, adding ~500 ATK for 65 Warrior (from 1150 to 1650). The previous calc didn't account for the primary skill value in the client calc.
4- Fixed stat calcs and added in stat caps. The previous stat calcs added in item and spell bonuses in CalcBotStats, but then when Get[Stat]() from mob was used, it returned the stat value plus items and spell bonuses, thereby doubling the stats from items and spells. Moved these calcs to bot.cpp, and checked against the newly calculated stat caps to determine the actual stat value. I couldn't find any other places where the actual stat variable is used in place of Get[stat]() function, which may store an invalid (higher than cap) value.
5- Bots were created with HP regen of 1 (with a bonus of 3 if sitting). Item HPRegen was never added in. Spell regen worked, except in my test case that I explain below in fixing bot buffs where buffs were not added when the caster was not spawned. I added in the client HPRegen code, which calculates level based regen, then adds in item, spell, and aa bonuses. My 65 War went from 1 HP regen to 20 (7 for lvl 65, 5 AA, and 8 item HP regen).
6- Bot mana regen was completely off. I changed it to use the client calculations, which showed me that besides the calculations being off, when the regen was added in was also off. Bot mana regen was performed once in each tic_timer check (every 6 seconds), and used the same calc as HPRegen (1 base regen, with 3 bonus for sitting). Then, BotMeditate was run, and within, for each mana_timer check (which was every 2 seconds), mana regen was added again. The calculation took the meditate bonus, added in buff bonuses to the spellbonus list (but spell bonuses had already been calculated, thereby double counting spell mana regen). Item mana regen was added in, and AAs. The regen value was then divided by the BotManaRegen rule (default of 2.0). I assume this was done because of the insane mana regen seen because the mana regen was added in every 2 seconds, instead of every tic, tripling bot mana regen. The bot mana regen values now match what they should. I added in a recommended sql update to change the BotManaRegen rule to 1.0, which keeps the value what it should be. Values of 2.0 would be half mana regen (slow!), and 0.5 would be double mana regen. I also changed the rule check to set everything <= 0.0 to 1.0, so there would be no div by 0, and no negative numbers, but still allow values to be less than 1.0 (to speed up bot mana regen).
7- Bot buffs had a few issues that are fixed (only one outstanding issue I can think of, but I didn't have an immediate thought on how to fix, so I will work on that later). First, Bot's buffs did not work if the caster was no longer spawned (bot died, client logged, etc.), even though the buff was maintained on the bot. The bot's stats did not reflect its buffs if the bot that had casted the buff had camped. This is most obvious when all bots camp, and then are respawned. Even though it shows that the bot has buffs, it's stats do not reflect the buffs. Also, a somewhat related issue is that buffs are not applied to the bot until a spell was first cast on them or something else that caused CalcBotStats() to be called. While LoadBuffs was called within Bot::Spawn(), this was after Bot() had already called CalcBotStats(). I moved LoadBuffs up into Bot::Bot() before CalcBotStats, which now spawns the bot with all current buffs and their stats added. The issue that still remains is if instead of calling #Bot Camp, you zone causing your spawned bots to poof, their buffs are not saved.
8- Since many of the Mob calculations for stats were incorrect, and I didn't want to affect anything elsewhere, I moved the calculations to Bot and added the #bot showstats command to use the bot calcs, and also threw in Caryatis' new stats window for free. (Besides the Mob calc issues, I wanted to keep all of my changes within bot.h and bot.cpp so nothing else was affected, which would help the fixes get added, I think).
9- In the initial stat generation, Paladin's ATK calc was off, and instead of adding in 17 to the base attack, the attack rating was set to 17 (Attack =+ 17; instead of Attack += 17; ) (actually, I'm not sure what =+ does, but I noticed my Paladin's ATK rating was much lower until I fixed this).
10- Changed it so that any client SoF+ used the new item links that are returned by the inventory list command.
1- Added SoD HP/Mana calculations for bots of clients who use SoD+
2- Changed AC calculations to match client calcs
3- Changed ATK calculations to match client calcs
4- Added stat caps and removed double counting of stats from items & spells
5- Fixed HP regen
6- Fixed Mana regen
7- Fixed buffs bugs
8- Added #bot showstats comand that uses the new ShowStats window (thanks Caryatis)
9- Fixed Paladin bot ATK bug
10- Fixed item links for inventory list command for SoD+ clients
Recommended SQL
UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen';
bot.h
--- EQEmuServer/zone/bot.h (rev 1742) Mon Nov 22 00:41:04 2010
+++ C:/EqEmuSource/EQEmuServer/zone/bot.h Sat Nov 20 22:45:03 2010
@@ -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,28 @@
bool IsStanding();
bool IsBotCasterCombatRange(Mob *target);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ;
+ int16 MaxSkill(SkillType skillid, int16 class_, int16 level) const;
+ 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();
+ virtual void ShowStats(Client* client);
// AI Methods
virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
@@ -263,7 +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); }
+ 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);
virtual void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; }
@@ -336,6 +411,7 @@
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();
bot.cpp
--- EQEmuServer/zone/bot.cpp (Rev 1742) Mon Nov 22 11:08:27 2010
+++ C:/EqEmulator/EQEmuServer/zone/bot.cpp Mon Nov 22 11:02:38 2010
@@ -120,11 +120,13 @@
}
GenerateBaseStats();
- GenerateArmorClass();
// Calculate HitPoints Last As It Uses Base Stats
GenerateBaseHitPoints();
+ // Load saved buffs
+ LoadBuffs();
+
CalcBotStats(false);
}
@@ -409,7 +411,7 @@
Wisdom += 15;
Charisma += 10;
Dexterity += 5;
- Attack =+ 17;
+ Attack += 17;
DiseaseResist += 8;
break;
case 4: // Ranger
@@ -763,19 +765,491 @@
}
+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;
}
+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(13);
+
+ if (!equiped)
+ skill = HAND_TO_HAND;
+
+ else {
+
+ uint8 type = m_inv.GetItem(13)->GetItem()->ItemType; //is this the best way to do this?
+
+ switch (type)
+ {
+ case ItemType1HS: // 1H Slashing
+ {
+ skill = _1H_SLASHING;
+ break;
+ }
+ case ItemType2HS: // 2H Slashing
+ {
+ skill = _2H_SLASHING;
+ break;
+ }
+ case ItemTypePierce: // Piercing
+ {
+ skill = PIERCING;
+ break;
+ }
+ case ItemType1HB: // 1H Blunt
+ {
+ skill = _1H_BLUNT;
+ break;
+ }
+ case ItemType2HB: // 2H Blunt
+ {
+ skill = _2H_BLUNT;
+ break;
+ }
+ 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;
+ }
+ }
+ }
+
+ return GetSkill(skill);
+}
+
+sint16 Bot::GetATK() {
+ int16 AttackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ if(WornCap > 250)
+ WornCap = 250;
+
+ AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69));
+
+ if (AttackRating < 10)
+ AttackRating = 10;
+
+ return AttackRating;
+}
+
void Bot::GenerateBaseHitPoints() {
// Calc Base Hit Points
/*int16 multiplier = 1;
@@ -810,21 +1284,52 @@
break;
}
int16 lm = multiplier;*/
+ 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;
+ Client* c = this->GetOwner()->CastToClient();
+ if(c && c->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;
}
void Bot::GenerateAABonuses() {
+ memset(&aabonuses, 0, sizeof(StatBonuses));
// General AA bonus
uint8 botlevel = GetLevel();
if(botlevel >= 51) {
@@ -1696,10 +2201,10 @@
// SetHP(GetHP()+hp_regen);
if(GetHP() < GetMaxHP())
- SetHP(GetHP() + hp_regen + bonus);
+ SetHP(GetHP() + CalcHPRegen() + bonus);
if(GetMana() < GetMaxMana())
- SetMana(GetMana() + mana_regen + bonus);
+ SetMana(GetMana() + CalcManaRegen() + bonus);
}
if (sendhpupdate_timer.Check()) {
@@ -1731,127 +2236,20 @@
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();
+ //SetAppearance(eaSitting, false);
}
else {
- SetAppearance(eaStanding, false);
+ if(IsSitting())
+ Stand();
+ //SetAppearance(eaStanding, false);
}
}
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;
-
- 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())
+ Stand();
+ //SetAppearance(eaStanding, false);
}
}
@@ -2842,9 +3240,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();
@@ -7122,41 +7517,100 @@
sint32 Bot::CalcMaxMana() {
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+ Client* c = this->GetOwner()->CastToClient();
switch (GetCasterClass()) {
case 'I':
WisInt = GetINT();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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));
+ }
+ max_mana = base_mana + wisint_mana;
+ max_mana += (itembonuses.Mana + spellbonuses.Mana);
}
- else {
- MindLesserFactor = 0;
+ else{
+ 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);
}
- 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);
break;
case 'W':
WisInt = GetWIS();
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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));
+ }
+ max_mana = base_mana + wisint_mana;
+ max_mana += (itembonuses.Mana + spellbonuses.Mana);
}
- else {
- MindLesserFactor = 0;
+ else{
+ 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);
}
- 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);
break;
default:
@@ -7771,8 +8225,7 @@
}
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);
+ Mob::DoBuffTic(spell_id, ticsremaining, caster_level, caster);
}
bool Bot::CastSpell(int16 spell_id, int16 target_id, int16 slot, sint32 cast_time, sint32 mana_cost, int32* oSpellWillFinish, int32 item_slot) {
@@ -8026,40 +8479,98 @@
sint32 bot_mana = 0;
sint32 WisInt = 0;
sint32 MindLesserFactor, MindFactor;
+ int wisint_mana = 0;
+ int base_mana = 0;
+ int ConvertedWisInt = 0;
+ Client* c = this->GetOwner()->CastToClient();
+
switch (GetCasterClass()) {
case 'I':
WisInt = INT;
- if((( WisInt - 199 ) / 2) > 0) {
- MindLesserFactor = ( WisInt - 199 ) / 2;
+ if (c && c->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 (c && c->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;
@@ -8195,20 +8706,535 @@
void Bot::CalcBonuses() {
CalcSpellBonuses(&spellbonuses);
CalcMaxHP();
- CalcMaxMana();
}
+sint32 Bot::CalcHPRegenCap(){
+ int level = GetLevel();
+ sint32 hpregen_cap = 0;
+ hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25;
+ if(level >= 71) {
+ //level 71 = 1 AA level
+ uint8 botAAlevels = level - 70;
+ hpregen_cap += botAAlevels * 1; // Energetic Attunement AAs
+ }
+#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;
+ }
+ if(level >= 85) {
+ uint8 botAAlevels = 25;
+ manaregen_cap += botAAlevels * 1; // Expansive Mind AAs
+ }
+ else if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ manaregen_cap += botAAlevels * 1; // Expansive Mind AAs
+ }
+#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();
+ Client* c = this->GetOwner()->CastToClient();
+
+ sint16 base = 0;
+
+ if (level < 61) {
+ base = 255;
+ }
+ else if (c && c->GetClientVersion() >= EQClientSoF) {
+ base = 255 + 5 * (level - 60);
+ }
+ else if (level < 71) {
+ base = 255 + 5 * (level - 60);
+ }
+ else {
+ base = 330;
+ }
+
+ if(level >= 81) {
+ uint8 botAAlevels = 20;
+ base += botAAlevels * 5; // Planar Power AAs
+ }
+ else if(level >= 61) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ base += botAAlevels * 5; // Planar Power AAs
+ }
+
+ return(base);
+}
+
+sint16 Bot::GetMaxResist() {
+ int level = GetLevel();
+
+ sint16 base = 500;
+
+ if(level > 60)
+ base += ((level - 60) * 5);
+
+ if(level >= 80) {
+ uint8 botAAlevels = 15;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+ else if(level >= 76) {
+ uint8 botAAlevels = 10;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+ else if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ base += botAAlevels * 5; // Discordant Defiance AAs
+ }
+
+ 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;
+ if(this->IsBotCaster() && (level >= 61)) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ aaINTCapMod += botAAlevels * 10; // Innate Enlightenment AAs
+ }
+
+ return GetMaxStat()
+ + itembonuses.INTCapMod
+ + spellbonuses.INTCapMod
+ + aaINTCapMod;
+}
+sint16 Bot::GetMaxWIS() {
+ sint16 aaWISCapMod = 0;
+ if(this->IsBotCaster() && (level >= 61)) {
+ //level 61 = 1 AA level
+ uint8 botAAlevels = level - 60;
+ aaWISCapMod += botAAlevels * 10; // Innate Enlightenment AAs
+ }
+
+ 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;
+}
+
+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);
+ //AAs
+ if(level >= 51) {
+ uint8 botAAlevels = 3;
+ regen += botAAlevels * 1; // Innate Regeneration AAs
+ }
+
+ if(level >= 61) {
+ uint8 botAAlevels = 2;
+ regen += botAAlevels * 1; // Convalescence AAs
+ }
+
+ if(level >= 66) {
+ //level 66 = 1 AA level
+ uint8 botAAlevels = level - 65;
+ if(botAAlevels > 15)
+ botAAlevels = 15;
+ regen += botAAlevels * 1; // Healthy Aura, Natural Healing, Body and Mind Rejuvenation AAs
+ }
+
+ regen = (regen * 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;
+ }
+
+ //AAs
+ if(level >= 55) {
+ uint8 botAAlevels = 3;
+ regen += botAAlevels * 1; // Mental Clarity AAs
+ }
+
+ if(level >= 85) {
+ uint8 botAAlevels = 23;
+ regen += botAAlevels * 1; // TSS, SoF, SoD, Underfoot AAs
+ }
+ else if(level >= 71) {
+ uint8 botAAlevels = level - 70;
+ regen += botAAlevels * 1; // TSS, SoF, SoD AAs
+ }
+
+ if(GetCasterClass() == 'I')
+ regen += (itembonuses.HeroicINT / 25);
+ else if(GetCasterClass() == 'W')
+ regen += (itembonuses.HeroicWIS / 25);
+ else
+ regen = 0;
+
+ regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100;
+
+ float mana_regen_rate = RuleR(Bots, BotManaRegen);
+ if(mana_regen_rate <= 0.0f)
+ mana_regen_rate = 1.0f;
+
+ regen = regen / mana_regen_rate;
+
+ 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() {
- int16 Post255 = 0;
sint32 bot_hp = 0;
- int16 lm = GetClassLevelFactor();
+ Client* c = this->GetOwner()->CastToClient();
+ if(c && c->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
+ float SoDPost255;
+ int16 NormalSTA = GetSTA();
- if((STA-255)/2 > 0)
- Post255 = (STA-255)/2;
- else
- Post255 = 0;
+ if(((NormalSTA - 255) / 2) > 0)
+ SoDPost255 = ((NormalSTA - 255) / 2);
+ else
+ SoDPost255 = 0;
- bot_hp = (5)+(GetLevel()*lm/10) + (((STA-Post255)*GetLevel()*lm/3000));
+ int hp_factor = GetClassHPFactor();
+
+ if (level < 41) {
+ bot_hp = (5 + (GetLevel() * hp_factor / 12) +
+ ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600));
+ }
+ else if (level < 81) {
+ bot_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800));
+ }
+ else {
+ bot_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) +
+ ((NormalSTA - SoDPost255) * hp_factor / 90) +
+ ((NormalSTA - SoDPost255) * hp_factor / 45));
+ }
+ }
+ else{
+ int16 Post255 = 0;
+ int16 NormalSTA = GetSTA();
+ int16 lm = GetClassLevelFactor();
+
+ if((NormalSTA-255)/2 > 0)
+ Post255 = (NormalSTA-255)/2;
+ else
+ Post255 = 0;
+
+ bot_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*GetLevel())*lm/6000);
+ }
bot_hp += itembonuses.HP;
// Hitpoint AA's
@@ -8615,59 +9641,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;
@@ -8808,47 +9782,76 @@
}
}
- 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();
+}
+// 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();
+
+ //// 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
+
+ /*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();
cur_hp = CalcMaxHP();
- GenerateBaseManaPoints();
+ cur_mana = CalcMaxMana();
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 ());
}
}
@@ -8932,6 +9935,7 @@
c->Message(0, "#bot botgroup help - Displays the commands available to manage BOT ONLY groups.");
c->Message(0, "#bot mana [<bot name or target> | all] - Displays a mana report for all your spawned bots.");
c->Message(0, "#bot [hair|haircolor|beard|beardcolor|face|eyes|heritage |tattoo|details <value>] - Change your BOTs appearance.");
+ c->Message(0, "#bot showstats - Show details about your bot.");
// TODO:
// c->Message(0, "#bot illusion <bot/client name or target> - Enchanter Bot cast an illusion buff spell on you or your target.");
return;
@@ -9375,7 +10379,7 @@
char* itemLink = 0;
if((i == 0) || (i == 11) || (i == 13) || (i == 14) || (i == 21)) {
- if (c->GetClientVersion() == EQClientSoF)
+ if (c->GetClientVersion() >= EQClientSoF)
{
MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X",
0,
@@ -9411,7 +10415,7 @@
}
}
else {
- if (c->GetClientVersion() == EQClientSoF)
+ if (c->GetClientVersion() >= EQClientSoF)
{
MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X",
0,
@@ -12024,6 +13028,22 @@
}
return;
}
+
+ if(!strcasecmp(sep->arg[1], "showstats")) {
+ if (c->GetTarget() == 0)
+ c->Message(0, "Error: no target");
+ else if (!c->GetTarget()->IsBot()) {
+ c->Message(0, "Error: you must target a bot");
+ }
+ else{
+ Bot *b = c->GetTarget()->CastToBot();
+
+ if(b){
+ b->ShowStats(c);
+ }
+ }
+ return;
+ }
}
// franck: EQoffline
@@ -12428,4 +13448,94 @@
return;
}
+void Bot::ShowStats(Client* client) {
+
+ int16 attackRating = 0;
+ int16 WornCap = GetATKBonus();
+
+ attackRating = GetATK();
+
+ if(client && RuleB(Character, UseNewStatsWindow)) {
+
+ // Define the types of page breaks we need
+ const char indP[] = " ";
+ const char indS[] = " ";
+ const char indM[] = " ";
+ const char indL[] = " ";
+ const char div[] = " | ";
+
+ client->SendStatWindow(" "
+ /* 01 - Name */ " %s <c \"#357EC7\"> %s %s</c><br>"
+ /* 02 - L/C/R */ " %s Class: %i %s Level: %i %s %s Race: %i<br>"
+ /* 03 - AC/Atk */ " %s ATK: %i<br>"
+ /* 04 - Labels */ " %s AC: %i %s %s %s Items Spells AAs Total Cap<br>"
+ /* 05 - HP */ " %s HP: %s %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 06 - Mana */ " %s Mana: %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 07 - End. */ " %s End.: %s %i %s %i %s %i %s %i %s %i %s %i<br>"
+ /* 08 - LineBr */ " <br>"
+ /* 09 - STR/MR */ " %s STR: %i | + %i %s MR: %i | + %i<br>"
+ /* 10 - STA/CR */ " %s STA: %i | + %i %s CR: %i | + %i<br>"
+ /* 11 - AGI/FR */ " %s AGI: %i | + %i %s FR: %i | + %i<br>"
+ /* 12 - DEX/PR */ " %s DEX: %i | + %i %s PR: %i | + %i<br>"
+ /* 13 - INT/DR */ " %s INT: %i | + %i %s DR: %i | + %i<br>"
+ /* 14 - WIS/Cp */ " %s WIS: %i | + %i %s Cp: %i | + %i<br>"
+ /* 15 - CHA/Ph */ " %s CHA: %i | + %i %s Physical: %i<br>"
+ /* 16 - LineBr */ " <br>"
+ /* 17 - Avd/Hat */ " %s Avoidance: %i / %i %s Heal Amt.: %i / %i<br>"
+ /* 18 - Acc/Sdg */ " %s Accuracy: %i / %i %s Spell Dmg.: %i / %i<br>"
+ /* 19 - CE/Clar */ " %s Combat Effects: %i / %i %s Clair.: %i / %i<br>"
+ /* 20 - DSd/DSm */ " %s DoT Shield.: %i / %i %s DS Mit.: %i / %i<br>"
+ /* 21 - Shield */ " %s Shielding: %i / %i<br>"
+ /* 22 - SS */ " %s Spell Shield.: %i / %i<br>"
+ /* 23 - Strike */ " %s Strike.: %i / %i<br>"
+ /* 24 - SR */ " %s Stun Resist: %i / %i" ,
+ /* 01 - Name */ indL, GetName(), GetLastName(),
+ /* 02 - L/C/R */ indP, GetClass(), indM, GetLevel(), indS, indP, GetBaseRace(),
+ /* 03 - AC/Atk */ indP, GetATK(),
+ /* 04 - Labels */ indP, GetAC(), indS, indP, indP,
+ /* 05 - HP */ indP, indP, GetMaxHP(), indS, itembonuses.HPRegen, div, spellbonuses.HPRegen, div, aabonuses.HPRegen, div, CalcHPRegen(), div, CalcHPRegenCap(),
+ /* 06 - Mana */ indP, GetMaxMana(), indS, itembonuses.ManaRegen, div, spellbonuses.ManaRegen, div, aabonuses.ManaRegen, div, CalcManaRegen(), div, CalcManaRegenCap(),
+ /* 07 - End. */ indP, indP, 0, indS, 0, div, 0, div, 0, div, 0, div, 0,
+ /* 08 - LineBr */
+ /* 09 - STR/MR */ indP, GetSTR(), GetHeroicSTR(), indL, GetMR(), GetHeroicMR(),
+ /* 10 - STA/CR */ indP, GetSTA(), GetHeroicSTA(), indL, GetCR(), GetHeroicCR(),
+ /* 11 - AGI/FR */ indP, GetAGI(), GetHeroicAGI(), indL, GetFR(), GetHeroicFR(),
+ /* 12 - DEX/PR */ indP, GetDEX(), GetHeroicDEX(), indL, GetPR(), GetHeroicPR(),
+ /* 13 - INT/DR */ indP, GetINT(), GetHeroicINT(), indL, GetDR(), GetHeroicDR(),
+ /* 14 - WIS/Cp */ indP, GetWIS(), GetHeroicWIS(), indL, GetCorrup(), GetHeroicCorrup(),
+ /* 15 - CHA/Ph */ indP, GetCHA(), GetHeroicCHA(), indL, ((GetSTR() + GetSTA()) / 2),
+ /* 16 - LineBr */
+ /* 17 - Avd/Hat */ indP, GetAvoidance(), RuleI(Character, ItemAvoidanceCap), indS, GetHealAmt(), RuleI(Character, ItemHealAmtCap),
+ /* 18 - Acc/Sdg */ indP, GetAccuracy(), RuleI(Character, ItemAccuracyCap), indS, GetSpellDmg(), RuleI(Character, ItemSpellDmgCap),
+ /* 19 - CE/Clar */ indP, GetCombatEffects(), RuleI(Character, ItemCombatEffectsCap), indS, GetClair(), RuleI(Character, ItemClairvoyanceCap),
+ /* 20 - DSd/DSm */ indP, GetDoTShield(), RuleI(Character, ItemDoTShieldingCap), indM, GetDSMit(), RuleI(Character, ItemDSMitigationCap),
+ /* 21 - Shield */ indP, GetShielding(), RuleI(Character, ItemShieldingCap),
+ /* 22 - SS */ indP, GetSpellShield(), RuleI(Character, ItemSpellShieldingCap),
+ /* 23 - Strike */ indP, GetStrikeThrough(), RuleI(Character, ItemStrikethroughCap),
+ /* 24 - SR */ indP, GetStunResist(), RuleI(Character, ItemStunResistCap)
+ );
+ }
+ else {
+
+ client->Message(0, "Name: %s %s", GetName(), lastname);
+
+ client->Message(0, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), GetAC(), GetClass(), GetSize(), GetHaste());
+ client->Message(0, " HP: %i Max HP: %i HP Regen: %i", GetHP(), GetMaxHP(), CalcHPRegen());
+ client->Message(0, " Mana: %i Max Mana: %i Mana Regen: %i", GetMana(), GetMaxMana(), CalcManaRegen());
+ client->Message(0, " Total ATK: %i Worn/Spell ATK (Cap 250): %i Server Used ATK: %i", attackRating, GetATKBonus(), attackRating);
+ client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA());
+ client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR());
+ client->Message(0, " Race: %i BaseRace: %i Texture: %i HelmTexture: %i Gender: %i BaseGender: %i", GetRace(), GetBaseRace(), GetTexture(), GetHelmTexture(), GetGender(), GetBaseGender());
+
+ }
+
+ if (client->Admin() >= 100) {
+ client->Message(0, " EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i",
+ GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted);
+ if (this->IsAIControlled()) {
+ client->Message(0, " AIControlled: AggroRange: %1.0f AssistRange: %1.0f", GetAggroRange(), GetAssistRange());
+ }
+ }
+}
+
#endif
Changes explanation:
1- Bots owned by character who uses SoD+ will use the new HP/Mana calculations, adding a significant amount of HPs and Mana to the bots.
2- Changed the AC calculations to match the client calculations, adding ~400 AC to lvl 65 Warrior (from 1100 - 1500). The previous calculation used mob's GetAC which only adds item AC (bot double counted), spell AC, and the bot's base AC (which starts as 12) and multiplied it by 1.5 if the bot was a Warrior or Knight.
3- Changed the ATK calculations to match the client calculations, adding ~500 ATK for 65 Warrior (from 1150 to 1650). The previous calc didn't account for the primary skill value in the client calc.
4- Fixed stat calcs and added in stat caps. The previous stat calcs added in item and spell bonuses in CalcBotStats, but then when Get[Stat]() from mob was used, it returned the stat value plus items and spell bonuses, thereby doubling the stats from items and spells. Moved these calcs to bot.cpp, and checked against the newly calculated stat caps to determine the actual stat value. I couldn't find any other places where the actual stat variable is used in place of Get[stat]() function, which may store an invalid (higher than cap) value.
5- Bots were created with HP regen of 1 (with a bonus of 3 if sitting). Item HPRegen was never added in. Spell regen worked, except in my test case that I explain below in fixing bot buffs where buffs were not added when the caster was not spawned. I added in the client HPRegen code, which calculates level based regen, then adds in item, spell, and aa bonuses. My 65 War went from 1 HP regen to 20 (7 for lvl 65, 5 AA, and 8 item HP regen).
6- Bot mana regen was completely off. I changed it to use the client calculations, which showed me that besides the calculations being off, when the regen was added in was also off. Bot mana regen was performed once in each tic_timer check (every 6 seconds), and used the same calc as HPRegen (1 base regen, with 3 bonus for sitting). Then, BotMeditate was run, and within, for each mana_timer check (which was every 2 seconds), mana regen was added again. The calculation took the meditate bonus, added in buff bonuses to the spellbonus list (but spell bonuses had already been calculated, thereby double counting spell mana regen). Item mana regen was added in, and AAs. The regen value was then divided by the BotManaRegen rule (default of 2.0). I assume this was done because of the insane mana regen seen because the mana regen was added in every 2 seconds, instead of every tic, tripling bot mana regen. The bot mana regen values now match what they should. I added in a recommended sql update to change the BotManaRegen rule to 1.0, which keeps the value what it should be. Values of 2.0 would be half mana regen (slow!), and 0.5 would be double mana regen. I also changed the rule check to set everything <= 0.0 to 1.0, so there would be no div by 0, and no negative numbers, but still allow values to be less than 1.0 (to speed up bot mana regen).
7- Bot buffs had a few issues that are fixed (only one outstanding issue I can think of, but I didn't have an immediate thought on how to fix, so I will work on that later). First, Bot's buffs did not work if the caster was no longer spawned (bot died, client logged, etc.), even though the buff was maintained on the bot. The bot's stats did not reflect its buffs if the bot that had casted the buff had camped. This is most obvious when all bots camp, and then are respawned. Even though it shows that the bot has buffs, it's stats do not reflect the buffs. Also, a somewhat related issue is that buffs are not applied to the bot until a spell was first cast on them or something else that caused CalcBotStats() to be called. While LoadBuffs was called within Bot::Spawn(), this was after Bot() had already called CalcBotStats(). I moved LoadBuffs up into Bot::Bot() before CalcBotStats, which now spawns the bot with all current buffs and their stats added. The issue that still remains is if instead of calling #Bot Camp, you zone causing your spawned bots to poof, their buffs are not saved.
8- Since many of the Mob calculations for stats were incorrect, and I didn't want to affect anything elsewhere, I moved the calculations to Bot and added the #bot showstats command to use the bot calcs, and also threw in Caryatis' new stats window for free. (Besides the Mob calc issues, I wanted to keep all of my changes within bot.h and bot.cpp so nothing else was affected, which would help the fixes get added, I think).
9- In the initial stat generation, Paladin's ATK calc was off, and instead of adding in 17 to the base attack, the attack rating was set to 17 (Attack =+ 17; instead of Attack += 17; ) (actually, I'm not sure what =+ does, but I noticed my Paladin's ATK rating was much lower until I fixed this).
10- Changed it so that any client SoF+ used the new item links that are returned by the inventory list command.