View Single Post
  #1  
Old 01-20-2011, 01:36 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default COMMITTED: Bot Updates / Fixes V 2.0

Here is my official submission for bot updates fixes.. A full list of changes is in this thread http://www.eqemulator.org/forums/showthread.php?t=32867, but include SoD HP/Mana cals, rest regen, existing buffs working after spawning, and correct AC/ATK calcs. In addition to those, I have also fixed a couple of other things:

1- Hybrid bots were casting healing spells on mobs instead of nukes. This has been fixed.
2- Classes who can cast stun spells now will.

1746_optional_sql_bot_manaregen.sql
Code:
UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen';

mob.cpp
Code:
Index: mob.cpp
===================================================================
--- mob.cpp	(revision 1832)
+++ mob.cpp	(working copy)

@@ -4182,7 +4182,9 @@
 		israidgrouped = false;
 	}
 	isgrouped = v;
-	parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+	if(IsClient())
+		parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
 }
 
 void Mob::SetRaidGrouped(bool v)
@@ -4192,7 +4194,9 @@
 		isgrouped = false;
 	}
 	israidgrouped = v;
-	parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this->CastToClient());
+
+	if(IsClient())
+		parse->Event(EVENT_GROUP_CHANGE, 0, "", (NPC*)NULL, this);
 }
 
 sint16 Mob::GetCriticalChanceBonus(int16 skill, bool aa_bonus)

bot.h
Code:
Index: bot.h
===================================================================
--- bot.h		(revision 1832)
+++ bot.h		(working copy)

@@ -99,7 +99,8 @@
 	virtual void TryCriticalHit(Mob *defender, int16 skill, sint32 &damage);
 	virtual bool TryFinishingBlow(Mob *defender, SkillType skillinuse);
 	virtual void DoRiposte(Mob* defender);
-	inline virtual sint16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); }
+	inline virtual sint16 GetATK();
+	uint16 GetPrimarySkillValue();
 	virtual void MeleeMitigation(Mob *attacker, sint32 &damage, sint32 minhit);
 	virtual void DoSpecialAttackDamage(Mob *who, SkillType skill, sint32 max_damage, sint32 min_damage = 1, sint32 hate_override = -1);
 	virtual void TryBackstab(Mob *other);
@@ -109,6 +110,7 @@
 	virtual bool TryHeadShot(Mob* defender, SkillType skillInUse);
 	virtual sint32 CheckAggroAmount(int16 spellid);
 	virtual void CalcBonuses();
+	void CalcItemBonuses();
 	virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
 	virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
 	inline virtual bool IsPet() { return false; }
@@ -119,6 +121,7 @@
 	virtual sint32 CheckHealAggroAmount(int16 spellid, int32 heal_possible = 0);
 	virtual sint32 CalcMaxMana();
 	virtual void SetAttackTimer();
+	int32 GetClassHPFactor();
 	virtual sint32 CalcMaxHP();
 	bool DoFinishedSpellAETarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
 	bool DoFinishedSpellSingleTarget(int16 spell_id, Mob* spellTarget, int16 slot, bool &stopLogic);
@@ -137,6 +140,27 @@
 	bool IsStanding();
 	bool IsBotCasterCombatRange(Mob *target);
 	bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ;
+	inline virtual sint16  GetMaxStat();
+	inline virtual sint16  GetMaxResist();
+	inline virtual sint16  GetMaxSTR();
+    inline virtual sint16  GetMaxSTA();
+    inline virtual sint16  GetMaxDEX();
+    inline virtual sint16  GetMaxAGI();
+    inline virtual sint16  GetMaxINT();
+    inline virtual sint16  GetMaxWIS();
+    inline virtual sint16  GetMaxCHA();
+	inline virtual sint16  GetMaxMR();
+	inline virtual sint16  GetMaxPR();
+	inline virtual sint16  GetMaxDR();
+	inline virtual sint16  GetMaxCR();
+	inline virtual sint16  GetMaxFR();
+	inline virtual sint16  GetMaxCorrup();
+	sint32  CalcHPRegenCap();
+	sint32 	CalcManaRegenCap();
+	sint32	LevelRegen();
+	sint32	CalcHPRegen();
+	sint32	CalcManaRegen();
+	void CalcRestState();
 
 	// AI Methods
 	virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
@@ -233,6 +257,7 @@
 	static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster);
 	static std::string GetBotMagicianPetType(Bot* botCaster);
 	static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType);
+	static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType);
 	static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target);
 	static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
 
@@ -263,6 +288,57 @@
 	bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
 	BotRoleType GetBotRole() { return _botRole; }
 	bool IsBotCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+	bool IsBotINTCaster() { return (GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
+	bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); }
+	inline virtual sint16 GetAC()	{ return AC; }
+	inline virtual sint16 GetSTR(); 
+	inline virtual sint16 GetSTA(); 
+	inline virtual sint16 GetDEX(); 
+	inline virtual sint16 GetAGI(); 
+	inline virtual sint16 GetINT(); 
+	inline virtual sint16 GetWIS(); 
+	inline virtual sint16 GetCHA(); 
+	inline virtual sint16 GetMR(); 
+	inline virtual sint16 GetFR(); 
+	inline virtual sint16 GetDR(); 
+	inline virtual sint16 GetPR(); 
+	inline virtual sint16 GetCR(); 
+	inline virtual sint16 GetCorrup();
+		//Heroic
+	inline virtual sint16	GetHeroicSTR()	const { return itembonuses.HeroicSTR; }
+	inline virtual sint16	GetHeroicSTA()	const { return itembonuses.HeroicSTA; }
+	inline virtual sint16	GetHeroicDEX()	const { return itembonuses.HeroicDEX; }
+	inline virtual sint16	GetHeroicAGI()	const { return itembonuses.HeroicAGI; }
+	inline virtual sint16	GetHeroicINT()	const { return itembonuses.HeroicINT; }
+	inline virtual sint16	GetHeroicWIS()	const { return itembonuses.HeroicWIS; }
+	inline virtual sint16	GetHeroicCHA()	const { return itembonuses.HeroicCHA; }
+	inline virtual sint16	GetHeroicMR()	const { return itembonuses.HeroicMR; }
+	inline virtual sint16	GetHeroicFR()	const { return itembonuses.HeroicFR; }
+	inline virtual sint16	GetHeroicDR()	const { return itembonuses.HeroicDR; }
+	inline virtual sint16	GetHeroicPR()	const { return itembonuses.HeroicPR; }
+	inline virtual sint16	GetHeroicCR()	const { return itembonuses.HeroicCR; }
+	inline virtual sint16	GetHeroicCorrup()	const { return itembonuses.HeroicCorrup; }
+	// Mod2
+	inline virtual sint16	GetShielding()		const { return itembonuses.MeleeMitigation; }
+	inline virtual sint16	GetSpellShield()	const { return itembonuses.SpellShield; }
+	inline virtual sint16	GetDoTShield()		const { return itembonuses.DoTShielding; }
+	inline virtual sint16	GetStunResist()		const { return itembonuses.StunResist; }
+	inline virtual sint16	GetStrikeThrough()	const { return itembonuses.StrikeThrough; }
+	inline virtual sint16	GetAvoidance()		const { return itembonuses.AvoidMeleeChance; }
+	inline virtual sint16	GetAccuracy()		const { return itembonuses.HitChance; }
+	inline virtual sint16	GetCombatEffects()	const { return itembonuses.ProcChance; }
+	inline virtual sint16	GetDS()				const { return itembonuses.DamageShield; }
+	// Mod3
+	inline virtual sint16	GetHealAmt()		const { return itembonuses.HealAmt; }
+	inline virtual sint16	GetSpellDmg()		const { return itembonuses.SpellDmg; }
+	inline virtual sint16	GetClair()			const { return itembonuses.Clairvoyance; }
+	inline virtual sint16	GetDSMit()			const { return itembonuses.DSMitigation; }
+	
+	inline virtual sint16	GetSingMod()		const { return itembonuses.singingMod; }
+	inline virtual sint16	GetBrassMod()		const { return itembonuses.brassMod; }
+	inline virtual sint16	GetPercMod()		const { return itembonuses.percussionMod; }
+	inline virtual sint16	GetStringMod()		const { return itembonuses.stringedMod; }
+	inline virtual sint16	GetWindMod()		const { return itembonuses.windMod; }
 
 	// "SET" Class Methods
 	void SetBotSpellID(uint32 newSpellID);
@@ -315,6 +391,9 @@
 	int32 _lastZoneId;
 	bool _rangerAutoWeaponSelect;
 	BotRoleType _botRole;
+	unsigned int RestRegenHP;
+	unsigned int RestRegenMana;
+	Timer rest_timer;
 
 	// Private "base stats" Members
 	sint16 _baseMR;
@@ -336,12 +415,13 @@
 	int8 _baseGender;	// Bots gender. Necessary to preserve the original value otherwise it can be changed by illusions.
 
 	// Class Methods
+	sint16 acmod();
 	void GenerateBaseStats();
 	void GenerateAppearance();
 	void GenerateArmorClass();
-	void GenerateBaseHitPoints();
+	sint32 GenerateBaseHitPoints();
 	void GenerateAABonuses();
-	void GenerateBaseManaPoints();
+	sint32 GenerateBaseManaPoints();
 	void GenerateSpecialAttacks();
 	void SetBotID(uint32 botID);
 	bool CalcBotHitChance(Mob* target, SkillType skillinuse, int Hand);

bot.cpp
Code:
Index: bot.cpp
===================================================================
--- bot.cpp		(revision 1832)
+++ bot.cpp	(working copy)

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

botspellsai.cpp
Code:
Index: botspellsai.cpp
===================================================================
--- botspellsai.cpp		(revision 1832)
+++ botspellsai.cpp		(working copy)

@@ -209,8 +209,7 @@
 			break;
 							   }
 		case SpellType_Nuke: {
-			if(((MakeRandomInt(1, 100) <= iChance) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
-				&& ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER))))
+			if((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
 			{
 				if(!checked_los) {
 					if(!CheckLosFN(tar))
@@ -228,7 +227,17 @@
 				else if(botClass == WIZARD) {
 					botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar);
 				}
-			
+
+				if(botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER) {
+					if(botSpell.SpellId == 0){
+						int8 stunChance = (tar->IsCasting() ? 30: 15);
+
+						if(!tar->IsStunned() && ( botClass == PALADIN || (MakeRandomInt(1, 100) <= stunChance))){
+							botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target);
+						}
+					}
+				}
+
 				if(botSpell.SpellId == 0)
 					botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target);
 
@@ -1207,7 +1214,7 @@
 
 		for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
 			// Assuming all the spells have been loaded into this list by level and in descending order
-			if(IsPureNukeSpell(botSpellListItr->SpellId)) {
+			if(IsPureNukeSpell(botSpellListItr->SpellId) && IsDamageSpell(botSpellListItr->SpellId)) {
 				result.SpellId = botSpellListItr->SpellId;
 				result.SpellIndex = botSpellListItr->SpellIndex;
 				result.ManaCost = botSpellListItr->ManaCost;
@@ -1220,6 +1227,30 @@
 	return result;
 }
 
+BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) {
+	BotSpell result;
+	
+	result.SpellId = 0;
+	result.SpellIndex = 0;
+	result.ManaCost = 0;
+
+	if(botCaster) {
+		std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType);
+
+		for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
+			// Assuming all the spells have been loaded into this list by level and in descending order
+			if(IsStunSpell(botSpellListItr->SpellId)) {
+				result.SpellId = botSpellListItr->SpellId;
+				result.SpellIndex = botSpellListItr->SpellIndex;
+				result.ManaCost = botSpellListItr->ManaCost;
+				break;
+			}
+		}
+	}
+
+	return result;
+}
+
 BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) {
 	BotSpell result;
Reply With Quote