Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #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
  #2  
Old 01-20-2011, 02:04 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Oh, I almost forgot. I also changed the bot's taunt. It was set to always succeed (which seems illogical and caused aggro to bounce around a whole lot in raiding situations), but now it is set to actually check their skill level to see if it succeeds.

This seems to make mobs' targets follow the bot with more built up aggro as opposed to switching targets every time a taunt was used.

I think in my next round of changes, I will make taunt toggleable, since you don't always want them doing it.
Reply With Quote
  #3  
Old 01-20-2011, 11:58 PM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

I've got this running now. No crashes that i've found, but I did notice a difference with #showstats. My Monk vs Bot Monk I seem to be capped at 330 for most main stats at level 65 while the bot is well above that and seemingly no caps with some 400+. Strange that my stats in my inventory window have me capped at 305 in Titanium. I'm gonna commit this anyway, but take a look at the caps and see if you find anything.
__________________
The Realm
Reply With Quote
  #4  
Old 01-21-2011, 10:52 AM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

After reading pages and pages of stuff this week on the resist issues discussed on Sony's forums in June between whether resist mods were applied pre or post cap, and thinking about your post, I see an issue with how I use the caps. The bot's stats are saved as the pre-cap value, and then the cap applied in Get[stat], which is then used in the calculations. The proble is that any debuffs will be applied pre-cap, which may or may not effect the bot post cap (but will most likely not be the correct amount).

Since I no longer have to juggle multiple copies of the bot code files and can update from the SVN now, I should be able to fix this today or tomorrow, and completely cap the stats, which should also allow #showstats to work besides any client specific stuff.
Reply With Quote
  #5  
Old 01-23-2011, 04:49 PM
wolfwalkereci
Discordant
 
Join Date: Dec 2005
Posts: 435
Default

Thanks for this update. Really looking forward to being able to toggle off taunt. Bots just generate so much agro it is silly.
Keep up the good work.
Reply With Quote
  #6  
Old 01-24-2011, 02:08 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Thanks and you're welcome.

Here some fixes. All are against Rev1834, which shouldn't matter except for mob.cpp.
1- I fixed the stat caps issue noted, and now should display the correct stats at all times.
2- Removed doubling up of mana from items and spells. (Missed this before somehow)
3- Fixed a mess up in how the last patch was applied to bot spells ai (the change for getting a nuke was applied to get fast heal).
4- Changed ShowStats to display the correct amount for ATK for bots. All other stats should be correct.


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

@@ -155,6 +155,19 @@
 	inline virtual sint16  GetMaxCR();
 	inline virtual sint16  GetMaxFR();
 	inline virtual sint16  GetMaxCorrup();
+	sint16  CalcSTR();
+	sint16  CalcSTA();
+	sint16  CalcDEX();
+	sint16  CalcAGI();
+	sint16  CalcINT();
+	sint16  CalcWIS();
+	sint16  CalcCHA();
+    sint16	CalcMR();
+	sint16	CalcFR();
+	sint16	CalcDR();
+	sint16	CalcPR();
+	sint16	CalcCR();
+	sint16	CalcCorrup();
 	sint32  CalcHPRegenCap();
 	sint32 	CalcManaRegenCap();
 	sint32	LevelRegen();
@@ -290,20 +303,20 @@
 	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();
+	inline virtual sint16	GetAC()	{ return AC; }
+	inline virtual sint16	GetSTR()	const { return STR; }
+	inline virtual sint16	GetSTA()	const { return STA; }
+	inline virtual sint16	GetDEX()	const { return DEX; }
+	inline virtual sint16	GetAGI()	const { return AGI; }
+	inline virtual sint16	GetINT()	const { return INT; }
+	inline virtual sint16	GetWIS()	const { return WIS; }
+	inline virtual sint16	GetCHA()	const { return CHA; }
+	inline virtual sint16	GetMR()	const { return MR; }
+	inline virtual sint16	GetFR()	const { return FR; }
+	inline virtual sint16	GetDR()	const { return DR; }
+	inline virtual sint16	GetPR()	const { return PR; }
+	inline virtual sint16	GetCR()	const { return CR; }
+	inline virtual sint16	GetCorrup()	const { return Corrup; }
 	//Heroic
 	inline virtual sint16	GetHeroicSTR()	const { return itembonuses.HeroicSTR; }
 	inline virtual sint16	GetHeroicSTA()	const { return itembonuses.HeroicSTA; }


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

@@ -447,7 +447,7 @@
 				PoisonResist += 4;
 				DiseaseResist += 4;
 				break;
-			case 6: // DiseaseResistuid
+			case 6: // Druid
 				BotSpellID = 707;
 				Stamina += 15;
 				Wisdom += 35;
@@ -483,7 +483,7 @@
 				Charisma += 10;
 				Attack += 28;
 				break;
-			case 11: // NeColdResistomancer
+			case 11: // Necromancer
 				BotSpellID = 703;
 				Dexterity += 10;
 				Agility += 10;
@@ -8534,7 +8534,6 @@
 					bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
 				}
 			}
-			bot_mana += (itembonuses.Mana + spellbonuses.Mana);
 			break;
 
 		case 'W':
@@ -8578,7 +8577,6 @@
 					bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
 				}
 			}
-			bot_mana += (itembonuses.Mana + spellbonuses.Mana);
 			break;
 
 		default:
@@ -8714,7 +8712,28 @@
 }
 
 void Bot::CalcBonuses() {
+	GenerateBaseStats();
+	CalcItemBonuses();
 	CalcSpellBonuses(&spellbonuses);
+	GenerateAABonuses();
+
+	CalcSTR();
+	CalcSTA();
+	CalcDEX();
+	CalcAGI();
+	CalcINT();
+	CalcWIS();
+	CalcCHA();
+	
+	CalcMR();
+	CalcFR();
+	CalcDR();
+	CalcPR();
+	CalcCR();
+	CalcCorrup();
+
+	GenerateArmorClass();
+
 	CalcMaxHP();
 	CalcMaxMana();
 	hp_regen = CalcHPRegen();
@@ -8907,122 +8926,228 @@
 		+ spellbonuses.CorrupCapMod;
 }
 
-sint16	Bot::GetSTR() { 
-	sint16 max = GetMaxSTR();
+sint16 Bot::CalcSTR() {
 	sint16 val = STR + itembonuses.STR + spellbonuses.STR;
+	
+	//sint16 mod = aabonuses.STR;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//STR = val + mod;
+	STR = val;
+	
+	if(STR < 1)
+		STR = 1;
 
-	if(val > max)
-		val = max;
+	int m = GetMaxSTR();
+	if(STR > m)
+		STR = m;
+	
+	return(STR);
+}
 
-	return val; 
-}
-sint16	Bot::GetSTA() { 
-	sint16 max = GetMaxSTA();
+sint16 Bot::CalcSTA() {
 	sint16 val = STA + itembonuses.STA + spellbonuses.STA;
- 
-	if(val > max)
-		val = max;
+	
+	//sint16 mod = aabonuses.STA;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//STA = val + mod;
+	STA = val;
+	
+	if(STA < 1)
+		STA = 1;
 
-	return val; 
+	int m = GetMaxSTA();
+	if(STA > m)
+		STA = m;
+	
+	return(STA);
 }
-sint16	Bot::GetDEX() { 
-	sint16 max = GetMaxDEX();
-	sint16 val = DEX + itembonuses.DEX + spellbonuses.DEX;
 
-	if(val > max)
-		val = max;
+sint16 Bot::CalcAGI() {
+	sint16 val = AGI + itembonuses.AGI + spellbonuses.AGI;
+	//sint16 mod = aabonuses.AGI;
 
-	return val; 
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+
+	//AGI = val + mod;
+	AGI = val;
+
+	if(AGI < 1)
+		AGI = 1;
+
+	int m = GetMaxAGI();
+	if(AGI > m)
+		AGI = m;
+	
+	return(AGI);
 }
-sint16	Bot::GetAGI() { 
-	sint16 max = GetMaxAGI();
-	sint16 val = AGI + itembonuses.AGI + spellbonuses.AGI;
- 
-	if(val > max)
-		val = max;
 
-	return val; 
+sint16 Bot::CalcDEX() {
+	sint16 val = DEX + itembonuses.DEX + spellbonuses.DEX;
+	
+	//sint16 mod = aabonuses.DEX;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//DEX = val + mod;
+	DEX = val;
+	
+	if(DEX < 1)
+		DEX = 1;
+
+	int m = GetMaxDEX();
+	if(DEX > m)
+		DEX = m;
+	
+	return(DEX);
 }
-sint16	Bot::GetINT() { 
-	sint16 max = GetMaxINT();
+
+sint16 Bot::CalcINT() {
 	sint16 val = INT + itembonuses.INT + spellbonuses.INT;
- 
-	if(val > max)
-		val = max;
+	
+	//sint16 mod = aabonuses.INT;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//INT = val + mod;
+	INT = val;
+	
+	if(INT < 1)
+		INT = 1;
 
-	return val; 
+	int m = GetMaxINT();
+	if(INT > m)
+		INT = m;
+	
+	return(INT);
 }
-sint16	Bot::GetWIS() { 
-	sint16 max = GetMaxWIS();
+
+sint16 Bot::CalcWIS() {
 	sint16 val = WIS + itembonuses.WIS + spellbonuses.WIS;
+	
+	//sint16 mod = aabonuses.WIS;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//WIS = val + mod;
+	WIS = val;
+	
+	if(WIS < 1)
+		WIS = 1;
 
-	if(val > max)
-		val = max;
+	int m = GetMaxWIS();
+	if(WIS > m)
+		WIS = m;
+	
+	return(WIS);
+}
 
-	return val; 
-}
-sint16	Bot::GetCHA() { 
-	sint16 max = GetMaxCHA();
+sint16 Bot::CalcCHA() {
 	sint16 val = CHA + itembonuses.CHA + spellbonuses.CHA;
- 
-	if(val > max)
-		val = max;
+	
+	//sint16 mod = aabonuses.CHA;
+	
+	if(val>255 && GetLevel() <= 60)
+		val = 255;
+	//CHA = val + mod;
+	CHA = val;
+	
+	if(CHA < 1)
+		CHA = 1;
 
-	return val; 
+	int m = GetMaxCHA();
+	if(CHA > m)
+		CHA = m;
+	
+	return(CHA);
 }
-sint16	Bot::GetMR() { 
-	sint16 max = GetMaxMR();
-	sint16 val = MR + itembonuses.MR + spellbonuses.MR;
- 
-	if(val > max)
-		val = max;
 
-	return val; 
+//The AA multipliers are set to be 5, but were 2 on WR
+//The resistant discipline which I think should be here is implemented
+//in Mob::ResistSpell
+sint16	Bot::CalcMR()
+{
+	MR += itembonuses.MR + spellbonuses.MR;
+	//MR += (GetAA(aaInnateMagicProtection) + GetAA(aaMarrsProtection))*2;
+	
+	if(MR < 1)
+		MR = 1;
+
+	if(MR > GetMaxMR())
+		MR = GetMaxMR();
+
+	return(MR);
 }
-sint16	Bot::GetFR()	{ 
-	sint16 max = GetMaxFR();
-	sint16 val = FR + itembonuses.FR + spellbonuses.FR;
 
-	if(val > max)
-		val = max;
+sint16	Bot::CalcFR()
+{
+	FR += itembonuses.FR + spellbonuses.FR;
+	//FR += (GetAA(aaInnateFireProtection) + GetAA(aaWardingofSolusek))*2;
+	
+	if(FR < 1)
+		FR = 1;
+	
+	if(FR > GetMaxFR())
+		FR = GetMaxFR();
 
-	return val; 
+	return(FR);
 }
-sint16	Bot::GetDR() { 
-	sint16 max = GetMaxDR();
-	sint16 val = DR + itembonuses.DR + spellbonuses.DR;
- 
-	if(val > max)
-		val = max;
 
-	return val; 
+sint16	Bot::CalcDR()
+{
+	DR += itembonuses.DR + spellbonuses.DR;
+	//DR += (GetAA(aaInnateDiseaseProtection) + GetAA(aaBertoxxulousGift))*2;
+	
+	if(DR < 1)
+		DR = 1;
+
+	if(DR > GetMaxDR())
+		DR = GetMaxDR();
+
+	return(DR);
 }
-sint16	Bot::GetPR() { 
-	sint16 max = GetMaxPR();
-	sint16 val = PR + itembonuses.PR + spellbonuses.PR;
 
-	if(val > max)
-		val = max;
+sint16	Bot::CalcPR()
+{
+	PR += itembonuses.PR + spellbonuses.PR;
+	//PR += (GetAA(aaInnatePoisonProtection) + GetAA(aaShroudofTheFaceless))*2;
+	
+	if(PR < 1)
+		PR = 1;
 
-	return val; 
+	if(PR > GetMaxPR())
+		PR = GetMaxPR();
+
+	return(PR);
 }
-sint16	Bot::GetCR() { 
-	sint16 max = GetMaxCR();
-	sint16 val = CR + itembonuses.CR + spellbonuses.CR;
 
-	if(val > max)
-		val = max;
+sint16	Bot::CalcCR()
+{
+	CR += itembonuses.CR + spellbonuses.CR;
+	//CR += (GetAA(aaInnateColdProtection) + GetAA(aaBlessingofEci))*2;
+	
+	if(CR < 1)
+		CR = 1;
 
-	return val; 
+	if(CR > GetMaxCR())
+		CR = GetMaxCR();
+
+	return(CR);
 }
-sint16	Bot::GetCorrup() { 
-	sint16 max = GetMaxCorrup();
-	sint16 val = Corrup + itembonuses.Corrup + spellbonuses.Corrup;
 
-	if(val > max)
-		val = max;
+sint16	Bot::CalcCorrup()
+{
+	//Corrup = Corrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup;
+	Corrup = Corrup + itembonuses.Corrup + spellbonuses.Corrup;
+	
+	if(Corrup > GetMaxCorrup())
+		Corrup = GetMaxCorrup();
 
-	return val; 
+	return(Corrup);
 }
 
 void Bot::CalcRestState() {
@@ -9823,10 +9948,6 @@
 	if(GetBotOwner()->GetLevel() != GetLevel())
 		SetLevel(GetBotOwner()->GetLevel());
 
-	GenerateBaseStats();
-
-	GenerateAABonuses();
-
 	GenerateSpecialAttacks();
 
 	if(showtext) {
@@ -9835,22 +9956,12 @@
 		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();
+	CalcBonuses();
 	
 	AI_AddNPCSpells(this->GetBotSpellID());


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

@@ -889,7 +889,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) && IsDamageSpell(botSpellListItr->SpellId)) {
+			if(IsFastHealSpell(botSpellListItr->SpellId)) {
 				result.SpellId = botSpellListItr->SpellId;
 				result.SpellIndex = botSpellListItr->SpellIndex;
 				result.ManaCost = botSpellListItr->ManaCost;
@@ -1216,7 +1216,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;


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

@@ -1040,8 +1040,12 @@
 			client->Message(0, "  HP: %i  Max HP: %i",GetHP(), GetMaxHP());
 			client->Message(0, "  Mana: %i  Max Mana: %i", GetMana(), GetMaxMana());
 		}
-	
-		client->Message(0, "  Total ATK: %i  Worn/Spell ATK (Cap %i): %i  Server Used ATK: %i", this->CastToClient()->GetTotalATK(), RuleI(Character, ItemATKCap), GetATKBonus(), attackRating);
+		if(IsBot()){
+			client->Message(0, "  Total ATK: %i  Worn/Spell ATK (Cap %i): %i", this->CastToBot()->GetATK(), RuleI(Character, ItemATKCap), GetATKBonus());
+		}
+		else{
+			client->Message(0, "  Total ATK: %i  Worn/Spell ATK (Cap %i): %i  Server Used ATK: %i", this->CastToClient()->GetTotalATK(), RuleI(Character, ItemATKCap), 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());
 		if (c)
 			client->Message(0, "  hSTR: %i  hSTA: %i  hDEX: %i  hAGI: %i  hINT: %i  hWIS: %i  hCHA: %i", c->GetHeroicSTR(), c->GetHeroicSTA(), c->GetHeroicDEX(), c->GetHeroicAGI(), c->GetHeroicINT(), c->GetHeroicWIS(), c->GetHeroicCHA());
Reply With Quote
  #7  
Old 01-24-2011, 09:03 PM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

I committed everything except the mob.cpp change since that doesn't compile when compiling Debug instead of DebugBots.
__________________
The Realm
Reply With Quote
  #8  
Old 01-24-2011, 11:06 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Okay, thanks. I didn't even think about that. Thanks for the info.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 07:06 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3