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

Reply
 
Thread Tools Display Modes
  #1  
Old 02-24-2010, 12:08 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default Pet Focus + NPC Expanded Inventory + NPC Item Haste Fixes

Here is an updated Pet Focus fix that I wanted to put in a separate thread, as there were many other changes that I didn't want to get lost. Much of the description is copied from the other thread.



In pets.cpp I add a parameter to makepet to allow new pets to be created at a specified pet power level (suspend minion, zoning, etc.) Then, I take out the current focus code, and call virtual GetPetFocusEffects, so it works for both clients and bots. Then, after the pet is created, I set the pet's focus power level so it can be recalled later if needed. I then create a method to call a single CalcPetFocusEffects() method that can be called from both clients and bots (to keep all the calcs in one place). The CalcPetFocusEffects() method contains all of the focus effect code. I update HP, AC, min and max damage, regen, size, and accuracy. HP, AC, min and max damage are as close to live as I could get. The actual values seen vary by class pet (mage pet increase was average- mostly hps, while necro increased AC significantly more, and beastlord warders increased melee damage the most. These equations keep the values within observed live values all the way up to pet power 90, which won't likely be seen here unless SoD is added. Lower level pet focus numbers (shovel of ponz, broom of trilon, etc. numbers couldn't be found, but the numbers end up in line with the rest.
In Set and Get pet state, I use the added inventory slots to equal a full inventory. I also have a method to get beginning equipment that is summoned with the pet based on pet focus power or caster level.

Code:
--- pets.cpp	(revision 1126)
+++ pets.cpp	(working copy)
@@ -215,9 +215,13 @@
 */
 
 
-void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
+void Mob::MakePet(int16 spell_id, const char* pettype, sint16 petFocusPower, const char *petname) {
 	//see if we are a special type of pet (for command filtering)
 	PetType type = petOther;
+	int32 items[MAX_INVENTORY_SLOTS];
+	uint8 caster_level = this->GetLevel();
+	string errorMessage;
+
 	if(strncmp(pettype, "Familiar", 8) == 0) {
 		type = petFamiliar;
 	} else if(strncmp(pettype, "Animation", 9) == 0) {
@@ -226,7 +230,6 @@
 	
 	if(HasPet())
 		return;
-
 	
 	//lookup our pets table record for this type
 	PetRecord record;
@@ -248,16 +251,7 @@
 	NPCType *npc_type = new NPCType;
 	memcpy(npc_type, base, sizeof(NPCType));
 	
-	if (this->IsClient() && CastToClient()->GetFocusEffect(focusPetPower, spell_id) > 0)
-	{
-		npc_type->max_hp *= 1.20;
-		npc_type->cur_hp = npc_type->max_hp;
-		npc_type->AC *= 1.20;
-		npc_type->level += 1;
-		npc_type->min_dmg = (npc_type->min_dmg * 110 / 100);
-		npc_type->max_dmg = (npc_type->max_dmg * 110 / 100);
-		npc_type->size *= 1.15;
-	}
+	GetPetFocusEffects(npc_type, spell_id, petFocusPower);
 
 	switch (GetAA(aaElementalDurability))
 	{
@@ -354,9 +348,53 @@
 	//this takes ownership of the npc_type data
 	Pet *npc = new Pet(npc_type, this, type, spell_id);
 
+	npc->SetPetFocusPower(petFocusPower);
+
+	npc->GetPetFocusStartingEquipment(items, caster_level, petFocusPower, &errorMessage);
+	npc->CalcBonuses();
+	npc->cur_hp = npc->max_hp;
+	npc->SendHPUpdate();
+
 	entity_list.AddNPC(npc, true, true);
 	SetPetID(npc->GetID());
 }
+
+void Mob::GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &petFocusPower) {
+	
+	if(IsClient())
+	{
+		if(petFocusPower == 0)
+		{
+			petFocusPower = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
+		}
+
+		if(petFocusPower > 0)
+		{
+			CalcPetFocusEffects(npc_type, petFocusPower);
+		}
+	}
+}
+
+void Mob::CalcPetFocusEffects(NPCType *npc_type, sint16 petFocusPower) {
+		
+	if(petFocusPower < 15){
+            	npc_type->AC = (int16)floor((double)npc_type->AC * (petFocusPower/2+100)/100);
+            	npc_type->min_dmg += (int32)floor((double)petFocusPower/5);
+            	npc_type->max_dmg += (int32)floor((double)petFocusPower/5);
+	}
+	else{
+            	npc_type->AC += (int16)floor((.75*petFocusPower*petFocusPower)/5);
+            	npc_type->level += (int8)floor((double)2*(petFocusPower / 40));
+            	npc_type->min_dmg += (int32)floor((double)petFocusPower/4 - 2);
+            	npc_type->max_dmg = (int32)floor((double)npc_type->max_dmg * ((0.4*(petFocusPower-10)+100)/100));
+	}
+
+	npc_type->max_hp = (sint32)floor((double)npc_type->max_hp * ((0.35*petFocusPower)+100)/100);
+	npc_type->cur_hp = npc_type->max_hp;
+	npc_type->hp_regen = (sint32)floor((double)npc_type->hp_regen * ((petFocusPower/2)+100)/100);
+    npc_type->size *= (((petFocusPower/2)+100)/100);
+	npc_type->accuracy_rating += (petFocusPower + 25);
+}
 /* Angelox: This is why the pets ghost - pets were being spawned too far away from its npc owner and some
 into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I 
 could get while it still looked good). I also noticed this can happen if an NPC is spawned on the same spot of another or in a related bad spot.*/
@@ -444,7 +482,7 @@
 	
 	//save their items
 	int i;
-	memset(items, 0, sizeof(int32)*MAX_MATERIALS);
+	memset(items, 0, sizeof(int32)*MAX_INVENTORY_SLOTS);
 	i = 0;
 	
 	ItemList::iterator cur,end;
@@ -454,7 +492,7 @@
 		ServerLootItem_Struct* item = *cur;
 		items[i] = item->item_id;
 		i++;
-		if (i >= MAX_MATERIALS)
+		if (i >= MAX_INVENTORY_SLOTS)
 			break;
 		//dont need to save anything else... since these items only
 		//exist for the pet, nobody else can get at them AFAIK
@@ -485,7 +523,7 @@
 
 void NPC::SetPetState(SpellBuff_Struct *pet_buffs, int32 *items) {
 	//restore their buffs...
-	
+
 	int i;
 	for (i = 0; i < BUFF_COUNT; i++) {
 		for(int z = 0; z < BUFF_COUNT; z++) {
@@ -541,8 +579,10 @@
 	}
 	UpdateRuneFlags();
 
+	ClearItemList();
+
 	//restore their equipment...
-	for(i = 0; i < MAX_MATERIALS; i++) {
+	for(i = 0; i < MAX_INVENTORY_SLOTS; i++) {
 		if(items[i] == 0)
 			continue;
 		
@@ -554,3 +594,32 @@
 		}
 	}
 }
+
+void NPC::GetPetFocusStartingEquipment(int32 *items, uint8 casterLevel, sint16 focusPower, std::string* errorMessage) {
+	if(this->IsPet() && this->GetOwner()) {
+		char errbuf[MYSQL_ERRMSG_SIZE];
+		char* query = 0;
+		int i = 0;
+		MYSQL_RES* DatasetResult;
+		MYSQL_ROW DataRow;
+
+		if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT itemid FROM pet_focus_starting_equipment WHERE minlevel<=%i AND maxlevel>=%i AND minfocuspower<=%i AND maxfocuspower>=%i order by slotid", casterLevel, casterLevel, focusPower, focusPower), errbuf, &DatasetResult)) {
+			while(DataRow = mysql_fetch_row(DatasetResult)) {
+				items[i] = atoi(DataRow[0]);
+
+				const Item_Struct* item2 = database.GetItem(items[i]);
+				if (item2 && item2->NoDrop != 0) {
+					//dont bother saving item charges for now, NPCs never use them
+					//and nobody should be able to get them off the corpse..?
+					AddLootDrop(item2, &itemlist, 0, true, true);
+				}
+			}
+
+			mysql_free_result(DatasetResult);
+		}
+		else 
+			*errorMessage = std::string(errbuf);
+
+		safe_delete_array(query);
+	}
+}

The declarations in mob.h:

Code:
--- mob.h	(revision 1205)
+++ mob.h	(working copy)
@@ -500,7 +500,9 @@
 	int		CountDispellableBuffs();
 	bool	HasBuffIcon(Mob* caster, Mob* target, int16 spell_id);
 
-	virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
+	virtual void MakePet(int16 spell_id, const char* pettype, sint16 petFocusPower, const char *petname = NULL);
+	virtual void GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower);
+	void CalcPetFocusEffects(NPCType *npc_type, sint16 focusPower);
 //	inline void	MakePetType(int16 spell_id, const char* pettype, const char *petname = NULL) { MakePet(spell_id, pettype, petname); }	//for perl
 //	void	MakePet(int16 spell_id, int8 in_level, int8 in_class, int16 in_race, int8 in_texture = 0, int8 in_pettype = 0, float in_size = 0, int8 type = 0, int32 min_dmg = 0, int32 max_dmg = 0, const char *petname = NULL);

A few changes to account for added parameter to MakePet:

Code:
--- perl_mob.cpp	(revision 1205)
+++ perl_mob.cpp	(working copy)
@@ -1476,7 +1476,7 @@
 			name = (char *)SvPV_nolen(ST(3));
 		}
 
-		THIS->MakePet(spell_id, pettype, name);
+		THIS->MakePet(spell_id, pettype, 0, name);
 	}
 	XSRETURN_EMPTY;
 }

Code:
--- command.cpp	  (revision 1213)
+++ command.cpp	  (working copy)
@@ -2731,7 +2731,7 @@
 	if (sep->arg[1][0] == '\0')
 		c->Message(0, "Usage: #makepet pet_type_name (will not survive across zones)");
 	else
-		c->MakePet(0, sep->arg[1]);
+		c->MakePet(0, sep->arg[1], 0);
 }
 
 void command_level(Client *c, const Seperator *sep)
I also set the petFocusPower from the extprofile.
Code:
--- client_packet.cpp	(revision 1227)
+++ client_packet.cpp	(working copy)
@@ -8063,13 +8063,14 @@
 	//Remake pet
 	if (m_epp.pet_id > 1 && !GetPet() && m_epp.pet_id <= SPDAT_RECORDS)
 	{
-		MakePet(m_epp.pet_id, spells[m_epp.pet_id].teleport_zone, m_epp.pet_name);
+		MakePet(m_epp.pet_id, spells[m_epp.pet_id].teleport_zone, m_epp.petFocusPower, m_epp.pet_name);
 		if (GetPet() && GetPet()->IsNPC()) {
 			NPC *pet = GetPet()->CastToNPC();
 			pet->SetPetState(m_epp.pet_buffs, m_epp.pet_items);
 			pet->CalcBonuses();
 			pet->SetHP(m_epp.pet_hp);
 			pet->SetMana(m_epp.pet_mana);
+			pet->SetPetFocusPower(m_epp.petFocusPower);
 		}
 		m_epp.pet_id = 0;
 	}
This one also makes a change to the SE_LimitMaxLevel in CalcFocusEffects() due to the fact that pet focii (as well as some other focus effects) have a 0 in the per level falloff over the max level, which designates that they should stop working altogether. They could be set to 100 in the database to achieve the same effect, but this one change keeps from having to maintain those changes, as any new focus effects pulled from live (lucy) would have the 0, and would need to be changed.

Code:
--- spell_effects.cpp	(revision 1126)
+++ spell_effects.cpp	(working copy)
@@ -1048,7 +1048,7 @@
 				}
 				else
 				{
-					MakePet(spell_id, spell.teleport_zone);
+					MakePet(spell_id, spell.teleport_zone, 0);
 				}
 				break;
 			}
@@ -3580,7 +3580,7 @@
 
 			if(lvldiff > 0){ //every level over cap reduces the effect by spell.base2[i] percent
 				lvlModifier -= spell.base2[i]*lvldiff;
-				if(lvlModifier < 1)
+				if((lvlModifier < 1) || (spell.base2[i] == 0))
 					return 0;
 			}
 			break;

Next, is the code to save petFocusPower within the pet, as well as the array for the new equipment for a full inventory. I keep these separate from the currently used equipment array, as it is used for visual materials, and I didn't want to have to rework all of the slotIDs, which is different from item slot IDs.

Code:
--- npc.h	(revision 1175)
+++ npc.h	(working copy)
@@ -119,6 +119,7 @@
 
 	void	GetPetState(SpellBuff_Struct *buffs, int32 *items, char *name);
 	void	SetPetState(SpellBuff_Struct *buffs, int32 *items);
+	void	GetPetFocusStartingEquipment(int32 *items, uint8 casterLevel, sint16 focusPower, std::string* errorMessage);
 	void	InteractiveChat(int8 chan_num, int8 language, const char * message, const char* targetname,Mob* sender);
 	void	TakenAction(int8 action,Mob* actiontaker);
 	virtual void SpellProcess();
@@ -201,6 +202,8 @@
 	bool	IsAnimal() const { return(bodytype == BT_Animal); }
 	int16   GetPetSpellID() const {return pet_spell_id;}
 	void    SetPetSpellID(int16 amt) {pet_spell_id = amt;}
+	sint16  GetPetFocusPower() const {return pet_focus_power;}
+	void    SetPetFocusPower(sint16 amt) {pet_focus_power = amt;}
 	int32	GetMaxDamage(int8 tlevel);
 	void    SetTaunting(bool tog) {taunting = tog;}
 	void	PickPocket(Client* thief);
@@ -336,6 +339,7 @@
 	
 	//pet crap:
 	int16	pet_spell_id;
+	sint16	pet_focus_power;
 	bool	taunting;
     Timer	taunt_timer;		//for pet taunting
 	
@@ -361,6 +365,7 @@
 	
 	int16   skills[HIGHEST_SKILL+1];
 	int32   equipment[MAX_MATERIALS];	//this is an array of item IDs
+	int32   equipped_items[MAX_INVENTORY_SLOTS];	//this is an array of item IDs
 	int16	d_meele_texture1;		//this is an item Material value
 	int16	d_meele_texture2;		//this is an item Material value (offhand)
Declaration for Bot's version of GetPetFocusEffects()

Code:
--- bot.h	(revision 1132)
+++ bot.h	(working copy)
@@ -113,6 +113,7 @@
 	virtual sint32 CheckAggroAmount(int16 spellid);
 	virtual void CalcBonuses();
 	virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
+	virtual void GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower);
 	virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
 	inline virtual bool IsPet() { return false; }
 	virtual bool IsNPC() const { return false; }
Bot code to get BotfocusPetPower. (It couldn't be accessed from within mob.cpp). It also updates Get- and Set- PetStates for the increased inventory slots.


Code:
--- bot.cpp	(revision 1180)
+++ bot.cpp	(working copy)
@@ -1253,6 +1253,7 @@
 		int16 petMana = 0;
 		int16 petHitPoints = 0;
 		uint32 botPetId = 0;
+		sint16 petFocusPower = 0;
 
 		LoadPetStats(&petName, &petMana, &petHitPoints, &botPetId, PetSaveId);
 
@@ -1261,7 +1262,7 @@
 		if(GetPet() && GetPet()->IsNPC()) {
 			NPC *pet = GetPet()->CastToNPC();
 			SpellBuff_Struct petBuffs[BUFF_COUNT];
-			int32 petItems[MAX_MATERIALS];
+			int32 petItems[MAX_INVENTORY_SLOTS];
 
 			LoadPetBuffs(petBuffs, PetSaveId);
 			LoadPetItems(petItems, PetSaveId);
@@ -1413,10 +1414,11 @@
 		NPC *pet = GetPet()->CastToNPC();
 		int16 petMana = pet->GetMana();
 		int16 petHitPoints = pet->GetHP();
+		sint16 petFocusPower = pet->GetPetFocusPower();
 		uint32 botPetId = pet->CastToNPC()->GetPetSpellID();
 		char* tempPetName = new char[64];
 		SpellBuff_Struct petBuffs[BUFF_COUNT];
-		int32 petItems[MAX_MATERIALS];
+		int32 petItems[MAX_INVENTORY_SLOTS];
 
 		pet->GetPetState(petBuffs, petItems, tempPetName);
 		
@@ -1498,7 +1500,7 @@
 		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
 		int ItemCount = 0;
 
-		while(ItemCount < MAX_MATERIALS) {
+		while(ItemCount < MAX_INVENTORY_SLOTS) {
 			if(petItems[ItemCount] > 0) {
 				if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetinventory (BotPetsId, ItemId) VALUES(%u, %u);", botPetSaveId, petItems[ItemCount]), TempErrorMessageBuffer)) {
 					errorMessage = std::string(TempErrorMessageBuffer);
@@ -6792,9 +6794,22 @@
 }
 
 void Bot::MakePet(int16 spell_id, const char* pettype, const char *petname) {
-	Mob::MakePet(spell_id, pettype, petname);
+	Mob::MakePet(spell_id, pettype, 0, petname);
 }
 
+void Bot::GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower) {
+	
+	if(IsBot())
+	{
+		focusPower = GetBotFocusEffect(BotfocusPetPower, spell_id);
+
+		if(focusPower > 0)
+		{
+			CalcPetFocusEffects(npc_type, focusPower);
+		}
+	}
+}
+
 void Bot::AI_Stop() {
 	NPC::AI_Stop();
 	Mob::AI_Stop();
Change to the player profile struct to store the petFocusPower to allow unsuspended pets to retain their power if the caster had removed the focus item after summoning, just like live.
Code:

Code:
--- eq_packet_structs.h	(revision 1208)
+++ eq_packet_structs.h	(working copy)
@@ -767,8 +767,9 @@
 	uint16			SpellID;
 	uint32			HP;
 	uint32			Mana;
+	sint16			PetFocusPower;
 	SpellBuff_Struct	Buffs[BUFF_COUNT];
-	uint32			Items[MAX_MATERIALS];
+	uint32			Items[MAX_INVENTORY_SLOTS];
 	char			Name[64];
 };
 
@@ -1004,8 +1005,8 @@
 /*12804*/	uint32				aapoints;	//avaliable, unspent
 /*12808*/	uint8				unknown12808[36];
 /*12844*/	Bandolier_Struct	bandoliers[MAX_PLAYER_BANDOLIER];
-/*14124*/	uint8				unknown14124[4506];
-/*18630*/	SuspendedMinion_Struct		SuspendedMinion;
+/*14124*/	uint8				unknown14124[4452];
+/*18576*/	SuspendedMinion_Struct		SuspendedMinion;
 /*19240*/	uint32				timeentitledonaccount;
 /*19244*/	PotionBelt_Struct	potionbelt;	//there should be 3 more of these
 /*19532*/	uint8				unknown19532[8];

Change to client.cpp to suspend and unsuspend pet, saving and restoring pet with same pet level.
Code:
--- client.cpp	(revision 1207)
+++ client.cpp	(working copy)
@@ -540,6 +540,7 @@
 		m_epp.pet_id = pet->CastToNPC()->GetPetSpellID();
 		m_epp.pet_hp = pet->GetHP();
 		m_epp.pet_mana = pet->GetMana();
+		m_epp.petFocusPower = pet->GetPetFocusPower();
 		pet->GetPetState(m_epp.pet_buffs, m_epp.pet_items, m_epp.pet_name);
 	} else {
 		m_epp.pet_id = 0;
@@ -5608,7 +5609,7 @@
 	{
 		if(m_pp.SuspendedMinion.SpellID > 0)
 		{
-			MakePet(m_pp.SuspendedMinion.SpellID, spells[m_pp.SuspendedMinion.SpellID].teleport_zone);
+			MakePet(m_pp.SuspendedMinion.SpellID, spells[m_pp.SuspendedMinion.SpellID].teleport_zone, m_pp.SuspendedMinion.PetFocusPower);
 
 			CurrentPet = GetPet()->CastToNPC();
 
@@ -5630,6 +5631,8 @@
 
 			CurrentPet->SetMana(m_pp.SuspendedMinion.Mana);
 
+			CurrentPet->SetPetFocusPower(m_pp.SuspendedMinion.PetFocusPower);
+
 			Message_StringID(clientMessageTell, SUSPEND_MINION_UNSUSPEND, CurrentPet->GetCleanName());
 			
 			memset(&m_pp.SuspendedMinion, 0, sizeof(SuspendedMinion_Struct));
@@ -5664,10 +5667,12 @@
 			{
 				m_pp.SuspendedMinion.SpellID = SpellID;
 
-				m_pp.SuspendedMinion.HP = CurrentPet->GetHP();;
+				m_pp.SuspendedMinion.HP = CurrentPet->GetHP();
 
 				m_pp.SuspendedMinion.Mana = CurrentPet->GetMana();
 
+				m_pp.SuspendedMinion.PetFocusPower = CurrentPet->GetPetFocusPower();
+
 				if(AALevel >= 2)
 					CurrentPet->GetPetState(m_pp.SuspendedMinion.Buffs, m_pp.SuspendedMinion.Items, m_pp.SuspendedMinion.Name);
Change to eq_constants for maximum number of slots:
Code:
--- eq_constants.h	(revision 1161)
+++ eq_constants.h	(working copy)
@@ -587,7 +587,8 @@
 #define MATERIAL_FEET		6
 #define MATERIAL_PRIMARY	7
 #define MATERIAL_SECONDARY	8
-#define MAX_MATERIALS 9	//number of equipables
+#define MAX_MATERIALS 9	//number of visible equipables
+#define MAX_INVENTORY_SLOTS 22	//number of equipables

Edit npc to maintain the new array of items.
Code:
--- npc.cpp	(revision 1205)
+++ npc.cpp	(working copy)
@@ -256,6 +256,7 @@
 	d_meele_texture1 = d->d_meele_texture1;
 	d_meele_texture2 = d->d_meele_texture2;
 	memset(equipment, 0, sizeof(equipment));
+	memset(equipped_items, 0, sizeof(equipped_items));
 	
 	//give NPCs skill values...
 	int r;

Change to extprofile to save pet power and items through zone and after logging.
Code:
--- extprofile.h	(revision 2)
+++ extprofile.h	(working copy)
@@ -40,8 +40,9 @@
 	int16				pet_id;
 	int16				pet_hp;
 	int16				pet_mana;
+	sint16				petFocusPower;
 	SpellBuff_Struct	pet_buffs[BUFF_COUNT];
-	int32				pet_items[MAX_MATERIALS];
+	int32				pet_items[MAX_INVENTORY_SLOTS];
 	char				pet_name[64];
 	
 	uint32				aa_effects;

In bonuses, I use the new array of items to calculate the bonuses, so they encompass all slots, not just the visible ones. I also allow npcs to benefit from item haste (required for pets, and I believe accurate for mobs as well).
Code:
--- bonuses.cpp	(revision 991)
+++ bonuses.cpp	(working copy)
@@ -1126,8 +1126,8 @@
 {
 	if(newbon){
 
-		for(int i = 0; i < 8; i++){
-			const Item_Struct *cur = database.GetItem(equipment[i]);
+		for(int i = 0; i < MAX_INVENTORY_SLOTS; i++){
+			const Item_Struct *cur = database.GetItem(equipped_items[i]);
 			if(cur){
 				//basic stats
 				newbon->AC += cur->AC;
@@ -1146,7 +1146,6 @@
 				newbon->CR += cur->CR;
 				newbon->PR += cur->PR;
 				newbon->DR += cur->DR;
-				
 
 				//more complex stats
 				if(cur->Regen > 0) {
@@ -1182,12 +1181,14 @@
 				if(cur->CombatEffects > 0) {
 					newbon->ProcChance += cur->CombatEffects;
 				}
+				if(cur->Haste > 0) {
+					newbon->haste += cur->Haste;
+				}
 				if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
 					ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon);
 				}
 			}
 		}
-	
 	}
 }

Change to loottables so that when npcs (and pets) are given loot, the item is placed in the new item array as well as the visible item array. From what I could tell, pets add 1 ear, neck, shoulder, back, face, range, wrist, waist, and 1 finger slots. I went ahead and added the code to allow them access to the 2nd ear, ring, and wrist slots, ammo, and charm slots, but commented them out if npcs actually can equip those slots (I saw conflicting comments about pet inventory).
Code:
--- loottables.cpp	(revision 1133)
+++ loottables.cpp	(working copy)
@@ -459,6 +459,7 @@
 	item->aug5 = 0;
 	if (equipit) {
 		uint8 eslot = 0xFF;
+		uint8 equippedslot = 0xFF;
 		//const Item_Struct* item2 = database.GetItem(item->item_id);
 		char newid[20];
 		if(!item2)
@@ -486,6 +487,7 @@
 				CastToMob()->AddProcToWeapon(item2->Proc.Effect, true);
 			
 			eslot = MATERIAL_PRIMARY;
+			equippedslot = SLOT_PRIMARY;
 		}
 		else if (item2->Slots & (1 << SLOT_SECONDARY) && (equipment[MATERIAL_SECONDARY]==0) 
 			&& (GetOwner() != NULL || (GetLevel() >= 13 && MakeRandomInt(0,99) < NPC_DW_CHANCE) || (item2->Damage==0)) &&
@@ -496,28 +498,73 @@
 				CastToMob()->AddProcToWeapon(item2->Proc.Effect, true);
 			
 			eslot = MATERIAL_SECONDARY;
+			equippedslot = SLOT_SECONDARY;
 		}
 		else if ((item2->Slots & (1 << SLOT_HEAD)) && (equipment[MATERIAL_HEAD]==0)) {
 			eslot = MATERIAL_HEAD;
+			equippedslot = SLOT_HEAD;
 		}
 		else if ((item2->Slots & (1 << SLOT_CHEST)) && (equipment[MATERIAL_CHEST]==0)) {
 			eslot = MATERIAL_CHEST;
+			equippedslot = SLOT_CHEST;
 		}
 		else if ((item2->Slots & (1 << SLOT_ARMS)) && (equipment[MATERIAL_ARMS]==0)) {
 			eslot = MATERIAL_ARMS;
+			equippedslot = SLOT_ARMS;
 		}
 		else if (item2->Slots & ((1 << SLOT_BRACER01)|(1 << SLOT_BRACER02)) && (equipment[MATERIAL_BRACER]==0)) {
 			eslot = MATERIAL_BRACER;
+			equippedslot = SLOT_BRACER01;
 		}
 		else if ((item2->Slots & (1 << SLOT_HANDS)) && (equipment[MATERIAL_HANDS]==0)) {
 			eslot = MATERIAL_HANDS;
+			equippedslot = SLOT_HANDS;
 		}
 		else if ((item2->Slots & (1 << SLOT_LEGS)) && (equipment[MATERIAL_LEGS]==0)) {
 			eslot = MATERIAL_LEGS;
+			equippedslot = SLOT_LEGS;
 		}
 		else if ((item2->Slots & (1 << SLOT_FEET)) && (equipment[MATERIAL_FEET]==0)) {
 			eslot = MATERIAL_FEET;
+			equippedslot = SLOT_FEET;
 		}
+		//get equipped items for the rest of the slots
+		else if ((item2->Slots & ((1 << SLOT_EAR01)|(1 << SLOT_EAR02)) && (equipped_items[SLOT_EAR01]==0))) {
+			equippedslot = SLOT_EAR01;
+		}
+		else if ((item2->Slots & (1 << SLOT_FACE)) && (equipped_items[SLOT_FACE]==0)) {
+			equippedslot = SLOT_FACE;
+		}
+		else if ((item2->Slots & (1 << SLOT_NECK)) && (equipped_items[SLOT_NECK]==0)) {
+			equippedslot = SLOT_NECK;
+		}
+		else if ((item2->Slots & (1 << SLOT_SHOULDER)) && (equipped_items[SLOT_SHOULDER]==0)) {
+			equippedslot = SLOT_SHOULDER;
+		}
+		else if ((item2->Slots & (1 << SLOT_RANGE)) && (equipped_items[SLOT_RANGE]==0)) {
+			equippedslot = SLOT_RANGE;
+		}
+		else if ((item2->Slots & ((1 << SLOT_RING01)|(1 << SLOT_RING02)) && (equipped_items[SLOT_RING01]==0))) {
+			equippedslot = SLOT_RING01;
+		}
+		else if ((item2->Slots & (1 << SLOT_WAIST)) && (equipped_items[SLOT_WAIST]==0)) {
+			equippedslot = SLOT_WAIST;
+		}
+		/*else if (item2->Slots & ((1 << SLOT_BRACER01)|(1 << SLOT_BRACER02)) && (equipped_items[SLOT_BRACER02]==0)) {
+			equippedslot = SLOT_BRACER02;
+		}
+		else if ((item2->Slots & (1 << SLOT_CHARM)) && (equipped_items[SLOT_CHARM]==0)) {
+			equippedslot = SLOT_CHARM;
+		}
+		else if ((item2->Slots & ((1 << SLOT_EAR01)|(1 << SLOT_EAR02)) && (equipped_items[SLOT_EAR02]==0))) {
+			equippedslot = SLOT_EAR02;
+		}
+		else if ((item2->Slots & ((1 << SLOT_RING01)|(1 << SLOT_RING02)) && (equipped_items[SLOT_RING02]==0))) {
+			equippedslot = SLOT_RING02;
+		}
+		else if ((item2->Slots & (1 << SLOT_AMMO)) && (equipped_items[SLOT_AMMO]==0)) {
+			equippedslot = SLOT_AMMO;
+		}*/
 		
 		/*
 		what was this about???
@@ -544,7 +591,13 @@
 				wc->wear_slot_id = eslot;
 				wc->material = emat;
 			}
-			
+		}
+
+		//if we found an open slot it goes in...
+		if(equippedslot != 0xFF) {
+			//equip it...
+			equipped_items[equippedslot] = item2->ID;
+
 			CalcBonuses();
 		}
 		item->equipSlot = item2->Slots;


Here is the required SQL code for the pet focus starting equipment:
Code:
--
-- Table structure for table `pet_focus_starting_equipment`
--

DROP TABLE IF EXISTS `pet_focus_starting_equipment`;
CREATE TABLE `pet_focus_starting_equipment` (
  `itemid` int(12) unsigned NOT NULL default '0',
  `slotid` mediumint(7) unsigned NOT NULL default '0',
  `minlevel` int(11) NOT NULL default '0',
  `maxlevel` int(11) NOT NULL default '255',
  `minfocuspower` int(11) NOT NULL default '0',
  `maxfocuspower` int(11) NOT NULL default '255',
  PRIMARY KEY  (`itemid`,`slotid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;


INSERT INTO `pet_focus_starting_equipment` ( itemid, slotid, minlevel, maxlevel, minfocuspower, maxfocuspower) VALUES 
-- level 48 < level 71, >= 10 power 
( 3419, 2, 48, 70, 10, 255), --Phantom Plate Helm
( 3421, 7, 48, 70, 10, 255), --Phantom Plate Vambraces
( 3423, 12, 48, 70, 10, 255), --Phantom Plate Gauntlets
( 3425, 19, 48, 70, 10, 255), --Phantom Plate Boots
( 3422, 9, 48, 70, 10, 255), --Phantom Plate Bracers
( 3420, 17, 48, 70, 10, 255), --Phantom Plate Breastplate
( 3424, 18, 48, 70, 10, 255), --Phantom Plate Greaves
( 1348, 3, 48, 70, 10, 255), --Muzzle of Mardu
( 28594, 20, 48, 58, 10, 255), --Belt of Magi'kot
( 28598, 20, 59, 70, 10, 255), --Girdle of Magi'kot
( 29796, 5, 61, 70, 10, 255), --Jedah's Platinum Choker
( 29797, 6, 61, 70, 10, 255), --Tavee's Runed Mantle
( 29799, 15, 61, 70, 10, 255), --Naki's Spiked Ring
( 29800, 1, 61, 70, 10, 255), --Jolum's Glowing Bauble
-- level 71 < level 76
( 46980, 2, 71, 75, 35, 255), --Eidolon Plate Helm
( 46981, 7, 71, 75, 35, 255), --Eidolon Plate Vambraces
( 46982, 12, 71, 75, 35, 255), --Eidolon Plate Gauntlets
( 46983, 19, 71, 75, 35, 255), --Eidolon Plate Boots
( 46984, 9, 71, 75, 35, 255), --Eidolon Plate Bracers
( 46985, 17, 71, 75, 35, 255), --Eidolon Plate Breastplate
( 46986, 18, 71, 75, 35, 255), --Eidolon Plate Greaves
( 46988, 20, 71, 75, 35, 255), --Summoned: Lucid Belt
( 46987, 3, 71, 75, 35, 255), --Muzzle of Mowcha
( 52666, 5, 71, 75, 35, 255), --Aenda's Satin Choker
( 52667, 6, 71, 75, 35, 255), --Aenda's Woven Shawl
( 52669, 15, 71, 75, 35, 255), --Aenda's Gold Ring
( 52670, 1, 71, 75, 35, 255), --Aenda's Ridged Earhoop
-- > level 76
( 52714, 2, 76, 255, 35, 255), --Prime Plate Helm
( 52715, 7, 76, 255, 35, 255), --Prime Plate Vambraces
( 52716, 12, 76, 255, 35, 255), --Prime Plate Gauntlets
( 52717, 19, 76, 255, 35, 255), --Prime Plate Boots
( 52718, 9, 76, 255, 35, 255), --Prime Plate Bracers
( 52719, 17, 76, 255, 35, 255), --Prime Plate Breastplate
( 52720, 18, 76, 255, 35, 255), --Prime Plate Greaves
( 52722, 20, 76, 255, 35, 255), --Summoned: Prime Belt
( 52721, 3, 76, 255, 35, 255), --Summoned: Binding of Brazaz
( 52727, 5, 76, 80, 35, 255), --Nastel's Satin Choker
( 52728, 6, 76, 80, 35, 255), --Nastel's Woven Shawl
( 52730, 15, 76, 80,  35, 255), --Nastel's Gold Ring
( 52731, 1, 76, 80, 35, 255), --Nastel's Ridged Earhoop
-- > level 81
( 52727, 5, 81, 255, 35, 255), --Zabella's Satin Choker
( 52728, 6, 81, 255, 35, 255), --Zabella's Woven Shawl
( 52730, 15, 81, 255, 35, 255), --Zabella's Gold Ring
( 52731, 1, 81, 255, 35, 255), --Zabella's Ridged Earhoop
-- 45 <= focusPower < 55 
( 52737, 8, 65, 255, 45, 54), --Cloak of the Spire
-- 55 <= focusPower < 65 
( 52739, 8, 71, 255, 55, 64), --Cloak of Enhancement I
-- 65 <= focusPower < 80 
( 52741, 8, 76, 255, 65, 79), --Cloak of Enhancement III 
-- 80 <= focusPower
( 52744, 8, 76, 255, 80, 255); --Cloak of Enhancement VI

Here's SQL code to extend the extprofile in character_ to allow for the pet power and extended inventory slots. It should keep the pet's buff, but clear the inventory, as slot ids have changed.
Code:
update character_ set extprofile = concat(left(extprofile, 6), repeat(char(0), 2), mid(extprofile, 6, 500), repeat(char(0), 88), right(extprofile, 64 + 4 + 4));
Reply With Quote
  #2  
Old 02-24-2010, 12:41 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

An example to show how the pet focus affects the pet. I was a lvl 75 Mage so I could use the Minion of Discord focus.

Lvl 60 Rathe's Sun (Base)
HP 4800
AC 1520
unhasted DPS (with 2 summoned pet weapons) 34.67

Lvl 60 Rathe's Sun (Focus - Minion of Discord: pet power 35, no equipment)
HP 5388 (12.25% increase)
AC 1703 (12.0% increase)
unhasted DPS (with 2 summoned pet weapons) 37.55 (8.3% increase)
DPS increase ~12% between hasted base and focused pets

Lvl 60 Rathe's Sun (Focus - Minion of Discord: pet power 35, with equipment)
HP 6978 (45.4% increase)
AC 1919 (26.25% increase)
unhasted DPS (with 2 summoned pet weapons, haste mask) 46.17 (33.2% increase - mostly due to haste mask)

While the differences aren't mindblowing, especially once the pet is equipped when unfocused, the biggest gain will be from chain casting pets or summoning a new pet in the middle of a fight with no time to give them equipment. The HP difference itself would be useful in raid situations.
Reply With Quote
  #3  
Old 02-24-2010, 02:38 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Also, not that anyone would have one yet, but the higher pet focus items summon pets with the high level cloaks with mod2s, including strikethrough. Here's a patch for that.

Code:
--- attack.cpp	(revision 1189)
+++ attack.cpp	(working copy)
@@ -1755,6 +1755,13 @@
 			//pets do half damage to clients in pvp
 			damage=damage/2;
 		}
+
+		if ((damage < 0) && !bRiposte) { 
+			if(MakeRandomInt(0, 100) < (itembonuses.StrikeThrough + spellbonuses.StrikeThrough)) {
+				Attack(other, Hand, true); // Strikethrough only gives another attempted hit
+				return false;
+			}
+		}
 	}
 	else
 		damage = -5;
This is assuming npcs can strikethrough as well.. If not, change with
Code:
--- attack.cpp	(revision 1189)
+++ attack.cpp	(working copy)
@@ -1755,6 +1755,16 @@
 			//pets do half damage to clients in pvp
 			damage=damage/2;
 		}
+
+		if(IsPet())
+		{
+			if ((damage < 0) && !bRiposte) { 
+				if(MakeRandomInt(0, 100) < (itembonuses.StrikeThrough + spellbonuses.StrikeThrough)) {
+					Attack(other, Hand, true); // Strikethrough only gives another attempted hit
+					return false;
+				}
+			}
+		}
 	}
 	else
 		damage = -5;
Reply With Quote
  #4  
Old 02-25-2010, 04:49 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Looks like pet classes are going to love you for this

My questions are:
1. In bonuses.cpp, what happens if the pet has multiple haste items? Do the hastes all stack or does it just use the highest value like haste should?

2. I don't know anything about pet focuses, but I assume they could work on charmed pets as well. If so, will your code that adds their loot to them but doesn't care about charges be able to cause any exploits? You can't get items with charges back from normal pet corpses, but you can from charmed ones.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #5  
Old 02-25-2010, 10:02 AM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Quote:
Originally Posted by trevius View Post
Looks like pet classes are going to love you for this

My questions are:
1. In bonuses.cpp, what happens if the pet has multiple haste items? Do the hastes all stack or does it just use the highest value like haste should?

2. I don't know anything about pet focuses, but I assume they could work on charmed pets as well. If so, will your code that adds their loot to them but doesn't care about charges be able to cause any exploits? You can't get items with charges back from normal pet corpses, but you can from charmed ones.
You're right about the haste. You can tell I was a caster!
This fixes that, so it's just like the client bonuses. Only picks the highest one. I also changed the loop so that it checks all slots but ammo, as I guess you don't get stats from ammo slot as mentioned in the client CalcItemBonuses?

Code:
--- bonuses.cpp	(revision 991)
+++ bonuses.cpp	(working copy)
@@ -1126,8 +1126,8 @@
 {
 	if(newbon){
 
-		for(int i = 0; i < 8; i++){
-			const Item_Struct *cur = database.GetItem(equipment[i]);
+		for(int i = 0; i < MAX_INVENTORY_SLOTS-1; i++){
+			const Item_Struct *cur = database.GetItem(equipped_items[i]);
 			if(cur){
 				//basic stats
 				newbon->AC += cur->AC;
@@ -1146,7 +1146,6 @@
 				newbon->CR += cur->CR;
 				newbon->PR += cur->PR;
 				newbon->DR += cur->DR;
-				
 
 				//more complex stats
 				if(cur->Regen > 0) {
@@ -1182,12 +1181,14 @@
 				if(cur->CombatEffects > 0) {
 					newbon->ProcChance += cur->CombatEffects;
 				}
+				if(newbon->haste < (sint8)cur->Haste) {
+					newbon->haste = cur->Haste;
+				}
 				if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent 

effects
 					ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon);
 				}
 			}
 		}
-	
 	}
 }
And I'm not sure about your question #2. Do you mean the equipment that automatically is summoned with the pet or just giving equipment to the charmed pet? Unless I'm mistaken, charmed pets will not get any of the pre-summoned equipment, and I don't believe they benefit from any focus effect. Only a summoned pet would gain from it. The focus affects the pet within MakePet, which I don't believe is used when an npc is charmed.

The only code I changed with regards to charmed pets would be that they now have an expanded inventory. The mechanism for giving the pet equipment remains the same, so I assume whatever precautions were taken into account before would still be in effect. I also believe no nodrop items can be given, but on that I'm not certain. Again, it should be just as it was before.

In AddLootDrop, a serverLootItem_Struct named item is created, assigned an id, and # of charges set.
Code:
item->item_id = item2->ID;
	item->charges = charges;
	item->aug1 = 0;
	item->aug2 = 0;
	item->aug3 = 0;
	item->aug4 = 0;
	item->aug5 = 0;
Then that item is pushed into the item list, which should contain the same number of charges.
Code:
if(itemlist != NULL)
		itemlist->push_back(item);
	else
		safe_delete(item);
I don't believe there was a restriction on the number of items a loottable can contain in its itemlist, but only a certain few would have been visible. When an npc is looted, it pulls from the itemlist. With the changes I've made, the only difference should be that besides the visible list of items (which doesn't affect the items available for loot), there is also a list of items that are just there to calculate bonuses and such from. Again, I don't believe this list would in any way affect what items can or can't be looted.

I may be incorrect, but I don't think that would be an issue.
Reply With Quote
  #6  
Old 02-25-2010, 03:30 PM
steve
Discordant
 
Join Date: Jan 2002
Posts: 305
Default

Quote:
Originally Posted by bad_captain View Post
And I'm not sure about your question #2. Do you mean the equipment that automatically is summoned with the pet or just giving equipment to the charmed pet? Unless I'm mistaken, charmed pets will not get any of the pre-summoned equipment, and I don't believe they benefit from any focus effect. Only a summoned pet would gain from it. The focus affects the pet within MakePet, which I don't believe is used when an npc is charmed.
This is correct. Charmed pets are not focused and do not get any equipment when they are charmed. As far as which pets get equipment when summoned, it used to be just mage pets -- I distinctly remember beastlords and necros asking for summoned weapons and armor. I'm not sure if it's changed since SoD was released.

Quote:
Originally Posted by bad_captain View Post
I also believe no nodrop items can be given, but on that I'm not certain.
You can give a pet a NO-DROP item, but they won't use or equip it. This is easily tested on Live by attempting to give a summoned pet a NO-DROP weapon.
Reply With Quote
  #7  
Old 02-25-2010, 03:49 PM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

According to the necro boards,http://www.necrotalk.com/showthread.php?t=5835 pets are at least getting equipment from Spire Servant and up. I don't mind changing it to just include Magicians for lower level focii, but really, besides the haste mask, there's nothing they wouldn't already be getting via stat increases, as I factored the equipment into the formulas for HP, AC, etc.

I doubt this will actually get committed as is anyway since it includes npc equipment and KLS stated she was planning on working on that after her spell updates, so I'm sure there will be room for changes.
Reply With Quote
  #8  
Old 02-25-2010, 08:27 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Cool, the haste fix looks good now. I don't really know a ton about new pet stuff on live, and didn't look into your code enough to really tell if charmed pets would be affected or not. I didn't think it would be an issue, but that was the only possibility of an exploit I could think of from your changes. Sounds like it should be just fine. My concern was if people gave a charmed pet an item with charges and then broke charm and killed it, the item might have full charges again. Definitely sounds like that is not the case, which is good.

And yeah, the main reason I don't want to add this myself is because I am not the most knowledgeable about how pets should work. I also am always cautious when messing with the size of the player profile or extended profile. It looks like the SQL you submitted should keep there from being any issues, but those blobs can definitely cause major headaches if something goes wrong. Also, I think you are right that KLS is planning to do work on this for her Spells branch, so it may be best to see what she gets done and go from there.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #9  
Old 02-25-2010, 09:00 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

My only concern is the pets that exist on the server will be corrupted by simply changing the blob structure like that.
Reply With Quote
  #10  
Old 02-26-2010, 12:41 AM
bad_captain
Developer
 
Join Date: Feb 2009
Location: Cincinnati, OH
Posts: 512
Default

Wouldn't the extprofile be okay with the supplied sql code, if it were run at the same time as the patch applied? I think the regular player profile would need the same type of sql code run (not expanding it, but just shifting the suspended minion struct back a little), and I tried to write it, but I came to the issue that the listed offsets in the the comments are incorrect, and I couldn't find the second error that gets the numbers back in line. The first was around 360 or so if I remember correctly. I didn't want to really mess something up, so that's why I posted on it in another thread a couple of days ago pointing out that the listed offsets were incorrect.

Anyway, I wanted to get this out there for anyone who wanted to play around with it, and now back to work on the bot group healing.
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 10:53 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