// Split from the basic MakePet to allow backward compatiblity with existing code while also // making it possible for petpower to be retained without the focus item having to // stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus // of a client is searched for and used instead. void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname, float in_size) { // Sanity and early out checking first. if(HasPet() || pettype == nullptr) return; int16 act_power = 0; // The actual pet power we'll use. if (petpower == -1) { if (this->IsClient()) { act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only act_power = CastToClient()->mod_pet_power(act_power, spell_id); } } else if (petpower > 0) act_power = petpower; // optional rule: classic style variance in pets. Achieve this by // adding a random 0-4 to pet power, since it only comes in increments // of five from focus effects. //lookup our pets table record for this type PetRecord record; if(!database.GetPoweredPetEntry(pettype, act_power, &record)) { Message(13, "Unable to find data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype); return; } //find the NPC data for the specified NPC type const NPCType *base = database.LoadNPCTypesData(record.npc_type); if(base == nullptr) { Message(13, "Unable to load NPC data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); return; } //we copy the npc_type data because we need to edit it a bit NPCType *npc_type = new NPCType; memcpy(npc_type, base, sizeof(NPCType)); // If pet power is set to -1 in the DB, use stat scaling if (this->IsClient() && record.petpower == -1) { float scale_power = (float)act_power / 100.0f; if(scale_power > 0) { npc_type->max_hp *= (1 + scale_power); npc_type->cur_hp = npc_type->max_hp; npc_type->AC *= (1 + scale_power); npc_type->level += 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2))); npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2))); npc_type->size *= (1 + (scale_power / 2)); } record.petpower = act_power; } //Live AA - Elemental Durability int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP; if (MaxHP){ npc_type->max_hp += (npc_type->max_hp*MaxHP)/100; npc_type->cur_hp = npc_type->max_hp; } //TODO: think about regen (engaged vs. not engaged) // Pet naming: // 0 - `s pet // 1 - `s familiar // 2 - `s Warder // 3 - Random name if client, `s pet for others // 4 - Keep DB name if (petname != nullptr) { // Name was provided, use it. strn0cpy(npc_type->name, petname, 64); } else if (record.petnaming == 0) { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } else if (record.petnaming == 1) { strcpy(npc_type->name, this->GetName()); npc_type->name[19] = '\0'; strcat(npc_type->name, "`s_familiar"); } else if (record.petnaming == 2) { strcpy(npc_type->name, this->GetName()); npc_type->name[21] = 0; strcat(npc_type->name, "`s_Warder"); } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { strcpy(npc_type->name, GetRandPetName()); } else { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } //handle beastlord pet appearance if(record.petnaming == 2) { switch(GetBaseRace()) { case VAHSHIR: npc_type->race = TIGER; npc_type->size *= 0.8f; break; case TROLL: npc_type->race = ALLIGATOR; npc_type->size *= 2.5f; break; case OGRE: npc_type->race = BEAR; npc_type->texture = 3; npc_type->gender = 2; break; case BARBARIAN: npc_type->race = WOLF; npc_type->texture = 2; break; case IKSAR: npc_type->race = WOLF; npc_type->texture = 0; npc_type->gender = 1; npc_type->size *= 2.0f; npc_type->luclinface = 0; break; default: npc_type->race = WOLF; npc_type->texture = 0; } } // handle monster summoning pet appearance if(record.monsterflag) { uint32 monsterid = 0; // get a random npc id from the spawngroups assigned to this zone auto query = StringFormat("SELECT npcID " "FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " "AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, " "55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, " "130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, " "380, 381, 382, 383, 404, 522) " "ORDER BY RAND() LIMIT 1", zone->GetShortName()); auto results = database.QueryDatabase(query); if (!results.Success()) { return; } if (results.RowCount() != 0) { auto row = results.begin(); monsterid = atoi(row[0]); } // since we don't have any monsters, just make it look like an earth pet for now if (monsterid == 0) monsterid = 567; // give the summoned pet the attributes of the monster we found const NPCType* monster = database.LoadNPCTypesData(monsterid); if(monster) { npc_type->race = monster->race; npc_type->size = monster->size; npc_type->texture = monster->texture; npc_type->gender = monster->gender; npc_type->luclinface = monster->luclinface; npc_type->helmtexture = monster->helmtexture; npc_type->herosforgemodel = monster->herosforgemodel; } else Log.Out(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); } //this takes ownership of the npc_type data Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower); // Now that we have an actual object to interact with, load // the base items for the pet. These are always loaded // so that a rank 1 suspend minion does not kill things // like the special back items some focused pets may receive. uint32 petinv[EmuConstants::EQUIPMENT_SIZE]; memset(petinv, 0, sizeof(petinv)); const Item_Struct *item = 0; if (database.GetBasePetItems(record.equipmentset, petinv)) { for (int i = 0; i<EmuConstants::EQUIPMENT_SIZE; i++) if (petinv[i]) { item = database.GetItem(petinv[i]); npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true); } } npc->UpdateEquipmentLight(); // finally, override size if one was provided if (in_size > 0.0f) npc->size = in_size; entity_list.AddNPC(npc, true, true); SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet if (record.petcontrol == petTargetLock) { Mob* target = GetTarget(); if (target){ npc->AddToHateList(target, 1); npc->SetPetTargetLockID(target->GetID()); npc->SetSpecialAbility(IMMUNE_AGGRO, 1); } else npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP) } }
// Split from the basic MakePet to allow backward compatiblity with existing code while also // making it possible for petpower to be retained without the focus item having to // stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus // of a client is searched for and used instead. void Mob::MakePoweredPet(int16 spell_id, const char* pettype, sint16 petpower, const char *petname) { // Sanity and early out checking first. if(HasPet() || pettype == NULL) return; sint16 act_power = 0; // lets see if this works to randomize pet levels // The actual pet power we'll use. if (petpower == 0) { // was == -1 if (this->IsClient()) act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id); } if (petpower == -1) { act_power = petpower + MakeRandomInt(0,5); } // optional rule: classic style variance in pets. Achieve this by // adding a random 0-4 to pet power, since it only comes in increments // of five from focus effects. //lookup our pets table record for this type PetRecord record; if(!database.GetPoweredPetEntry(pettype, act_power, &record)) { Message(13, "Unable to find data for pet %s", pettype); LogFile->write(EQEMuLog::Error, "Unable to find data for pet %s, check pets table.", pettype); return; } //find the NPC data for the specified NPC type const NPCType *base = database.GetNPCType(record.npc_type); if(base == NULL) { Message(13, "Unable to load NPC data for pet %s", pettype); LogFile->write(EQEMuLog::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); return; } //we copy the npc_type data because we need to edit it a bit NPCType *npc_type = new NPCType; memcpy(npc_type, base, sizeof(NPCType)); // If pet power is set to -1 in the DB, use stat scaling if (this->IsClient() && petpower == -1) { float scale_power = (float)act_power / 100.0f; if(scale_power > 0) { npc_type->max_hp *= (1 + scale_power); npc_type->cur_hp = npc_type->max_hp; npc_type->AC *= (1 + scale_power); npc_type->level += (float)scale_power * 50; //(int)act_power; // gains an additional level for every 25 pet power npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2))); npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2))); npc_type->size *= (1 + (scale_power / 2)); } petpower = act_power; } switch (GetAA(aaElementalDurability)) { case 1: npc_type->max_hp *= 1.02; npc_type->cur_hp = npc_type->max_hp; break; case 2: npc_type->max_hp *= 1.05; npc_type->cur_hp = npc_type->max_hp; break; case 3: npc_type->max_hp *= 1.10; npc_type->cur_hp = npc_type->max_hp; break; } //TODO: think about regen (engaged vs. not engaged) // Pet naming: // 0 - `s pet // 1 - `s familiar // 2 - `s Warder // 3 - Random name if client, `s pet for others // 4 - Keep DB name if (petname != NULL) { // Name was provided, use it. strn0cpy(npc_type->name, petname, 64); } else if (record.petnaming == 0) { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } else if (record.petnaming == 1) { strcpy(npc_type->name, this->GetName()); npc_type->name[19] = '\0'; strcat(npc_type->name, "`s_familiar"); } else if (record.petnaming == 2) { strcpy(npc_type->name, this->GetName()); npc_type->name[21] = 0; strcat(npc_type->name, "`s_Warder"); } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { strcpy(npc_type->name, GetRandPetName()); } else { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } //handle beastlord pet appearance if(record.petnaming == 2) { switch(GetBaseRace()) { case VAHSHIR: npc_type->race = TIGER; npc_type->size *= 0.8f; break; case TROLL: npc_type->race = ALLIGATOR; npc_type->size *= 2.5f; break; case OGRE: npc_type->race = BEAR; npc_type->texture = 3; npc_type->gender = 2; break; case BARBARIAN: npc_type->race = WOLF; npc_type->texture = 2; break; case IKSAR: npc_type->race = WOLF; npc_type->texture = 0; npc_type->gender = 1; npc_type->size *= 2.0f; npc_type->luclinface = 0; break; default: npc_type->race = WOLF; npc_type->texture = 0; } } // handle monster summoning pet appearance if(record.monsterflag) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES *result = NULL; MYSQL_ROW row = NULL; uint32 monsterid; // get a random npc id from the spawngroups assigned to this zone if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT npcID FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " "AND npc_types.race NOT IN (0,1,2,3,4,5,6,7,8,9,10,11,12,44,55,67,71,72,73,77,78,81,90,92,93,94,106,112,114,127,128,130,139,141,183,236,237,238,239,254,266,330,378,379,380,381,382,383,404,522) " "ORDER BY RAND() LIMIT 1", zone->GetShortName()), errbuf, &result)) { row = mysql_fetch_row(result); if (row) monsterid = atoi(row[0]); else monsterid = 567; // since we don't have any monsters, just make it look like an earth pet for now } else { // if the database query failed LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), errbuf); monsterid = 567; } // give the summoned pet the attributes of the monster we found const NPCType* monster = database.GetNPCType(monsterid); if(monster) { npc_type->race = monster->race; npc_type->size = monster->size; npc_type->texture = monster->texture; npc_type->gender = monster->gender; } else { LogFile->write(EQEMuLog::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); } safe_delete_array(query); } //this takes ownership of the npc_type data Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower); // Now that we have an actual object to interact with, load // the base items for the pet. These are always loaded // so that a rank 1 suspend minion does not kill things // like the special back items some focused pets may receive. int32 petinv[MAX_WORN_INVENTORY]; memset(petinv, 0, sizeof(petinv)); const Item_Struct *item = 0; if (database.GetBasePetItems(record.equipmentset, petinv)) { for (int i=0; i<MAX_WORN_INVENTORY; i++) if (petinv[i]) { item = database.GetItem(petinv[i]); npc->AddLootDrop(item, &npc->itemlist, 0, true, true); } } entity_list.AddNPC(npc, true, true); SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet }
// Split from the basic MakePet to allow backward compatiblity with existing code while also // making it possible for petpower to be retained without the focus item having to // stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus // of a client is searched for and used instead. void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname, float in_size) { // Sanity and early out checking first. bool scale_pet = false; if(HasPet() || pettype == nullptr) return; //lookup our pets table record for this type PetRecord record; if(!database.GetPoweredPetEntry(pettype, petpower, &record)) { Message(CC_Red, "Unable to find data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype); return; } //find the NPC data for the specified NPC type const NPCType *base = database.GetNPCType(record.npc_type); if(base == nullptr) { Message(CC_Red, "Unable to load NPC data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); return; } int act_power = 0; // The actual pet power we'll use. if (petpower == -1) { if (this->IsClient()) { //Message(CC_Red, "We are a client time to check for focus items"); uint16 focusItemId; FocusPetItem petItem; // Loop over all the focus items and figure out which on is the best to use // It will step down from PoP - Classic looking for the best focus item to use based on pet level int16 slot = 0; for(int i=0; i < FocusPetItemSize; i++) { petItem = Pet::focusItems[i]; // Look in out inventory int16 slot_id = this->CastToClient()->GetInv().HasItem(petItem.item_id, 1, invWhereWorn); if(slot_id != INVALID_INDEX) { //skip this focus item if its effect is out of rage for the pet we are casting if(base->level >= petItem.min_level && base->level <= petItem.max_level) { if(EQDEBUG>8) Message(CC_Red, "Found Focus Item: %d in Inventory: %d", petItem.item_id, slot_id); if(EQDEBUG>8) Message(CC_Red, "Npc spell levels: %d (%d - %d)", base->level, petItem.min_level, petItem.max_level); slot = slot_id; focusItemId = petItem.item_id; break; } else { if(EQDEBUG>8) Message(CC_Red, "Moving on Pet base level is out of range: %d (%d - %d)", base->level, petItem.min_level, petItem.max_level); } } } // we have a focus item if(focusItemId) { FocusPetType focusType; // Symbol or Gloves can be used by all NEC, MAG, BST if(petItem.pet_type == FocusPetType::ALL) { //Message(CC_Red, "Type is ALL"); focusType = FocusPetType::ALL; } else { // make sure we can use the focus item as the class .. client should never let us fail this but for sanity! if (GetClass() == MAGICIAN) { if(EQDEBUG>8) Message(CC_Red, "Looking up mage"); if(EQDEBUG>8) Message(CC_Red, "Looking up if spell: %d is allowed ot be focused", spell_id); focusType = Pet::GetPetItemPetTypeFromSpellId(spell_id); if(EQDEBUG>8) Message(CC_Red, "FocusType fround %i", focusType); } else if (GetClass() == NECROMANCER) { if(EQDEBUG>8) Message(CC_Red, "We are a necro"); focusType = FocusPetType::NECRO; } } // Sets the power to be what the focus item has as a mod if(EQDEBUG>8) { Message(CC_Red, "Pet Item Type ALL is %i", FocusPetType::ALL); Message(CC_Red, "Pet Item Type FIRE is %i", FocusPetType::FIRE); Message(CC_Red, "Pet Item Type WATER is %i", FocusPetType::WATER); Message(CC_Red, "Pet Item Type AIR is %i", FocusPetType::AIR); Message(CC_Red, "Pet Item Type EARTH is %i", FocusPetType::EARTH); Message(CC_Red, "Pet Item Type NECRO is %i", FocusPetType::NECRO); } if(EQDEBUG>8) Message(CC_Red, "Pet Item Type %i", petItem.pet_type); if (focusType == petItem.pet_type) { if(EQDEBUG>8) Message(CC_Red, "Setting power to: %d", petItem.power); act_power = petItem.power; scale_pet = true; } if(act_power > 0) { if(EQDEBUG>8) Message(CC_Yellow, "Debug: You have cast a powered pet. Unadjusted pet power is: %i. HasItem returned: %i.", act_power, slot); if(focusType == FocusPetType::NECRO ||focusType == FocusPetType::EARTH || focusType == FocusPetType::AIR || focusType == FocusPetType::FIRE || focusType == FocusPetType::WATER) { if(act_power > 10) act_power = 10; } else { if(act_power > 25) act_power = 25; } } } } } else if (petpower > 0) { act_power = petpower; scale_pet = true; } if(EQDEBUG>8) Message(CC_Red, "Power is: %d", act_power); // optional rule: classic style variance in pets. Achieve this by // adding a random 0-4 to pet power, since it only comes in increments // of five from focus effects. //we copy the npc_type data because we need to edit it a bit NPCType *npc_type = new NPCType; memcpy(npc_type, base, sizeof(NPCType)); // If pet power is set to -1 in the DB, use stat scaling if (this->IsClient() && record.petpower == -1) { if(scale_pet) { float scale_power = (float)act_power / 100.0f; if(scale_power > 0) { npc_type->max_hp = (int16) (npc_type->max_hp * (1 + scale_power)); npc_type->cur_hp = npc_type->max_hp; npc_type->AC = (int16) (npc_type->AC * (1 + scale_power)); npc_type->level += (int16) 1 + ((int16)act_power / 25); // gains an additional level for every 25 pet power npc_type->min_dmg = (int16) (npc_type->min_dmg * (1 + (scale_power / 2))); npc_type->max_dmg = (int16) (npc_type->max_dmg * (1 + (scale_power / 2))); npc_type->size = (npc_type->size * (1 + scale_power)); } record.petpower = act_power; } } //Live AA - Elemental Durability int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP; if (MaxHP){ npc_type->max_hp += (npc_type->max_hp*MaxHP)/100; npc_type->cur_hp = npc_type->max_hp; } //TODO: think about regen (engaged vs. not engaged) // Pet naming: // 0 - `s pet // 1 - `s familiar // 2 - `s Warder // 3 - Random name if client, `s pet for others // 4 - Keep DB name if (petname != nullptr) { // Name was provided, use it. strn0cpy(npc_type->name, petname, 64); } else if (record.petnaming == 0) { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } else if (record.petnaming == 1) { strcpy(npc_type->name, this->GetName()); npc_type->name[19] = '\0'; strcat(npc_type->name, "`s_familiar"); } else if (record.petnaming == 2) { strcpy(npc_type->name, this->GetName()); npc_type->name[21] = 0; strcat(npc_type->name, "`s_warder"); } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { strcpy(npc_type->name, GetRandPetName()); } else { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } //handle beastlord pet appearance if(record.petnaming == 2) { switch(GetBaseRace()) { case VAHSHIR: npc_type->race = TIGER; npc_type->size *= 0.8f; break; case TROLL: npc_type->race = ALLIGATOR; npc_type->size *= 2.5f; break; case OGRE: npc_type->race = BEAR; npc_type->texture = 3; npc_type->gender = 2; break; case BARBARIAN: npc_type->race = WOLF; npc_type->texture = 2; break; case IKSAR: npc_type->race = WOLF; npc_type->texture = 0; npc_type->gender = 1; npc_type->luclinface = 0; break; default: npc_type->race = WOLF; npc_type->texture = 0; } } // handle monster summoning pet appearance if(record.monsterflag) { uint32 monsterid = 0; // get a random npc id from the spawngroups assigned to this zone auto query = StringFormat("SELECT npcID " "FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " "AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, " "55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, " "130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, " "380, 381, 382, 383, 404, 522) " "ORDER BY RAND() LIMIT 1", zone->GetShortName()); auto results = database.QueryDatabase(query); if (!results.Success()) { // if the database query failed } if (results.RowCount() != 0) { auto row = results.begin(); monsterid = atoi(row[0]); } // since we don't have any monsters, just make it look like an earth pet for now if (monsterid == 0) monsterid = 567; // give the summoned pet the attributes of the monster we found const NPCType* monster = database.GetNPCType(monsterid); if(monster) { npc_type->race = monster->race; npc_type->size = monster->size; npc_type->texture = monster->texture; npc_type->gender = monster->gender; npc_type->luclinface = monster->luclinface; npc_type->helmtexture = monster->helmtexture; } else Log.Out(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); } //this takes ownership of the npc_type data Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower); // Now that we have an actual object to interact with, load // the base items for the pet. These are always loaded // so that a rank 1 suspend minion does not kill things // like the special back items some focused pets may receive. uint32 petinv[EmuConstants::EQUIPMENT_SIZE]; memset(petinv, 0, sizeof(petinv)); const Item_Struct *item = 0; if (database.GetBasePetItems(record.equipmentset, petinv)) { for (int i = 0; i<EmuConstants::EQUIPMENT_SIZE; i++) if (petinv[i]) { item = database.GetItem(petinv[i]); npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true); } } npc->UpdateEquipmentLight(); // finally, override size if one was provided if (in_size > 0.0f) npc->size = in_size; entity_list.AddNPC(npc, true, true); SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet }