PDA

View Full Version : Pet Focus + NPC Expanded Inventory + NPC Item Haste Fixes


bad_captain
02-24-2010, 12:08 PM
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.


--- 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:


--- 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:


--- 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;
}



--- 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.

--- 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.

--- 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.


--- 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()


--- 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.



--- 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:


--- 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.

--- 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.PetFocusPowe r);
+
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:

--- 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.

--- 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.

--- 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).

--- 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).

--- 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:

--
-- 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.

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));

bad_captain
02-24-2010, 12:41 PM
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.

bad_captain
02-24-2010, 02:38 PM
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.


--- 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

--- 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;

trevius
02-25-2010, 04:49 AM
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.

bad_captain
02-25-2010, 10:02 AM
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! :cool:
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?


--- 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.

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.

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.

steve
02-25-2010, 03:30 PM
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.

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.

bad_captain
02-25-2010, 03:49 PM
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.

trevius
02-25-2010, 08:27 PM
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.

KLS
02-25-2010, 09:00 PM
My only concern is the pets that exist on the server will be corrupted by simply changing the blob structure like that.

bad_captain
02-26-2010, 12:41 AM
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.