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