Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime) // vesuvias - appearence fix : Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),BT_Humanoid,//bodytype added in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),in_npc->GetSize(),0, in_npc->GetPosition(), in_npc->GetInnateLightType(), in_npc->GetTexture(),in_npc->GetHelmTexture(), 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0), corpse_decay_timer(in_decaytime), corpse_rez_timer(0), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(0), loot_cooldown_timer(10) { corpse_graveyard_timer.Disable(); memset(item_tint, 0, sizeof(item_tint)); is_corpse_changed = false; is_player_corpse = false; is_locked = false; being_looted_by = 0xFFFFFFFF; if (in_itemlist) { itemlist = *in_itemlist; in_itemlist->clear(); } SetCash(in_npc->GetCopper(), in_npc->GetSilver(), in_npc->GetGold(), in_npc->GetPlatinum()); npctype_id = in_npctypeid; SetPlayerKillItemID(0); char_id = 0; corpse_db_id = 0; player_corpse_depop = false; strcpy(corpse_name, in_npc->GetName()); strcpy(name, in_npc->GetName()); for(int count = 0; count < 100; count++) { if ((level >= npcCorpseDecayTimes[count].minlvl) && (level <= npcCorpseDecayTimes[count].maxlvl)) { corpse_decay_timer.SetTimer(npcCorpseDecayTimes[count].seconds*1000); break; } } if(IsEmpty()) { corpse_decay_timer.SetTimer(RuleI(NPC,EmptyNPCCorpseDecayTimeMS)+1000); } if(in_npc->HasPrivateCorpse()) { corpse_delay_timer.SetTimer(corpse_decay_timer.GetRemainingTime() + 1000); } for (int i = 0; i < MAX_LOOTERS; i++){ allowed_looters[i] = 0; } this->rez_experience = 0; UpdateEquipmentLight(); UpdateActiveLight(); }
void Corpse::RemoveItem(ServerLootItem_Struct* item_data) { for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { auto sitem = *iter; if (sitem != item_data) { continue; } is_corpse_changed = true; itemlist.erase(iter); uint8 material = Inventory::CalcMaterialFromSlot(sitem->equip_slot); // autos to unsigned char if (material != _MaterialInvalid) SendWearChange(material); UpdateEquipmentLight(); if (UpdateActiveLight()) SendAppearancePacket(AT_Light, GetActiveLightType()); safe_delete(sitem); return; } }
void Corpse::AddItem(uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, uint32 aug6, uint8 attuned) { if (!database.GetItem(itemnum)) return; is_corpse_changed = true; ServerLootItem_Struct* item = new ServerLootItem_Struct; memset(item, 0, sizeof(ServerLootItem_Struct)); item->item_id = itemnum; item->charges = charges; item->equip_slot = slot; item->aug_1=aug1; item->aug_2=aug2; item->aug_3=aug3; item->aug_4=aug4; item->aug_5=aug5; item->aug_6=aug6; item->attuned=attuned; itemlist.push_back(item); UpdateEquipmentLight(); }
// To be called from LoadFromDBData Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard) : Mob("Unnamed_Corpse", "", 0, 0, in_gender, in_race, in_class, BT_Humanoid, in_deity, in_level, 0, in_size, 0, position, 0, // verified for client innate_light value in_texture, in_helmtexture, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), loot_cooldown_timer(10) { LoadPlayerCorpseDecayTime(in_dbid); if (!zone->HasGraveyard() || wasAtGraveyard) corpse_graveyard_timer.Disable(); memset(item_tint, 0, sizeof(item_tint)); is_corpse_changed = false; is_player_corpse = true; is_locked = false; being_looted_by = 0xFFFFFFFF; corpse_db_id = in_dbid; player_corpse_depop = false; char_id = in_charid; itemlist = *in_itemlist; in_itemlist->clear(); strcpy(corpse_name, in_charname); strcpy(name, in_charname); this->copper = in_copper; this->silver = in_silver; this->gold = in_gold; this->platinum = in_plat; rez_experience = in_rezexp; for (int i = 0; i < MAX_LOOTERS; i++){ allowed_looters[i] = 0; } SetPlayerKillItemID(0); UpdateEquipmentLight(); m_Light.Level.Spell = m_Light.Type.Spell = 0; UpdateActiveLight(); }
Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( "Unnamed_Corpse", // const char* in_name, "", // const char* in_lastname, 0, // int32 in_cur_hp, 0, // int32 in_max_hp, client->GetGender(), // uint8 in_gender, client->GetRace(), // uint16 in_race, client->GetClass(), // uint8 in_class, BT_Humanoid, // bodyType in_bodytype, client->GetDeity(), // uint8 in_deity, client->GetLevel(), // uint8 in_level, 0, // uint32 in_npctype_id, client->GetSize(), // float in_size, 0, // float in_runspeed, client->GetPosition(), client->GetInnateLightType(), // uint8 in_light, - verified for client innate_light value client->GetTexture(), // uint8 in_texture, client->GetHelmTexture(), // uint8 in_helmtexture, 0, // uint16 in_ac, 0, // uint16 in_atk, 0, // uint16 in_str, 0, // uint16 in_sta, 0, // uint16 in_dex, 0, // uint16 in_agi, 0, // uint16 in_int, 0, // uint16 in_wis, 0, // uint16 in_cha, client->GetPP().haircolor, // uint8 in_haircolor, client->GetPP().beardcolor, // uint8 in_beardcolor, client->GetPP().eyecolor1, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? client->GetPP().eyecolor2, // uint8 in_eyecolor2, client->GetPP().hairstyle, // uint8 in_hairstyle, client->GetPP().face, // uint8 in_luclinface, client->GetPP().beard, // uint8 in_beard, client->GetPP().drakkin_heritage, // uint32 in_drakkin_heritage, client->GetPP().drakkin_tattoo, // uint32 in_drakkin_tattoo, client->GetPP().drakkin_details, // uint32 in_drakkin_details, 0, // uint32 in_armor_tint[_MaterialCount], 0xff, // uint8 in_aa_title, 0, // uint8 in_see_invis, // see through invis 0, // uint8 in_see_invis_undead, // see through invis vs. undead 0, // uint8 in_see_hide, 0, // uint8 in_see_improved_hide, 0, // int32 in_hp_regen, 0, // int32 in_mana_regen, 0, // uint8 in_qglobal, 0, // uint8 in_maxlevel, 0, // uint32 in_scalerate 0, // uint8 in_armtexture, 0, // uint8 in_bracertexture, 0, // uint8 in_handtexture, 0, // uint8 in_legtexture, 0 // uint8 in_feettexture, ), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), loot_cooldown_timer(10) { int i; PlayerProfile_Struct *pp = &client->GetPP(); ItemInst *item; /* Check if Zone has Graveyard First */ if(!zone->HasGraveyard()) { corpse_graveyard_timer.Disable(); } memset(item_tint, 0, sizeof(item_tint)); for (i = 0; i < MAX_LOOTERS; i++){ allowed_looters[i] = 0; } is_corpse_changed = true; rez_experience = in_rezexp; can_corpse_be_rezzed = true; is_player_corpse = true; is_locked = false; being_looted_by = 0xFFFFFFFF; char_id = client->CharacterID(); corpse_db_id = 0; player_corpse_depop = false; copper = 0; silver = 0; gold = 0; platinum = 0; strcpy(corpse_name, pp->name); strcpy(name, pp->name); /* become_npc was not being initialized which led to some pretty funky things with newly created corpses */ become_npc = false; SetPlayerKillItemID(0); /* Check Rule to see if we can leave corpses */ if(!RuleB(Character, LeaveNakedCorpses) || RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) { // cash // Let's not move the cash when 'RespawnFromHover = true' && 'client->GetClientVersion() < EQClientSoF' since the client doesn't. // (change to first client that supports 'death hover' mode, if not SoF.) if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < ClientVersion::SoF) { SetCash(pp->copper, pp->silver, pp->gold, pp->platinum); pp->copper = 0; pp->silver = 0; pp->gold = 0; pp->platinum = 0; } // get their tints memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint)); // TODO soulbound items need not be added to corpse, but they need // to go into the regular slots on the player, out of bags std::list<uint32> removed_list; for(i = MAIN_BEGIN; i < EmuConstants::MAP_POSSESSIONS_SIZE; ++i) { if(i == MainAmmo && client->GetClientVersion() >= ClientVersion::SoF) { item = client->GetInv().GetItem(MainPowerSource); if (item != nullptr) { if (!client->IsBecomeNPC() || (client->IsBecomeNPC() && !item->GetItem()->NoRent)) MoveItemToCorpse(client, item, MainPowerSource, removed_list); } } item = client->GetInv().GetItem(i); if (item == nullptr) { continue; } if(!client->IsBecomeNPC() || (client->IsBecomeNPC() && !item->GetItem()->NoRent)) MoveItemToCorpse(client, item, i, removed_list); } database.TransactionBegin(); // I have an untested process that avoids this snarl up when all possessions inventory is removed..but this isn't broke if (!removed_list.empty()) { std::stringstream ss(""); ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID(); ss << " AND ("; std::list<uint32>::const_iterator iter = removed_list.begin(); bool first = true; while (iter != removed_list.end()) { if (first) { first = false; } else { ss << " OR "; } ss << "slotid=" << (*iter); ++iter; } ss << ")"; database.QueryDatabase(ss.str().c_str()); } auto start = client->GetInv().cursor_cbegin(); auto finish = client->GetInv().cursor_cend(); database.SaveCursor(client->CharacterID(), start, finish); client->CalcBonuses(); client->Save(); IsRezzed(false); Save(); database.TransactionCommit(); UpdateEquipmentLight(); UpdateActiveLight(); return; } //end "not leaving naked corpses" UpdateEquipmentLight(); UpdateActiveLight(); IsRezzed(false); Save(); }
//if itemlist is null, just send wear changes void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, uint32 aug6) { if(item2 == nullptr) return; //make sure we are doing something... if(!itemlist && !wearchange) return; auto item = new ServerLootItem_Struct; #if EQDEBUG>=11 Log(Logs::General, Logs::None, "Adding drop to npc: %s, Item: %i", GetName(), item2->ID); #endif EQApplicationPacket* outapp = nullptr; WearChange_Struct* wc = nullptr; if(wearchange) { outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); wc = (WearChange_Struct*)outapp->pBuffer; wc->spawn_id = GetID(); wc->material=0; } item->item_id = item2->ID; item->charges = charges; item->aug_1 = aug1; item->aug_2 = aug2; item->aug_3 = aug3; item->aug_4 = aug4; item->aug_5 = aug5; item->aug_6 = aug6; item->attuned = 0; item->min_level = minlevel; item->max_level = maxlevel; item->equip_slot = EQEmu::inventory::slotInvalid; if (equipit) { uint8 eslot = 0xFF; char newid[20]; const EQEmu::ItemData* compitem = nullptr; bool found = false; // track if we found an empty slot we fit into int32 foundslot = -1; // for multi-slot items // Equip rules are as follows: // If the item has the NoPet flag set it will not be equipped. // An empty slot takes priority. The first empty one that an item can // fit into will be the one picked for the item. // AC is the primary choice for which item gets picked for a slot. // If AC is identical HP is considered next. // If an item can fit into multiple slots we'll pick the last one where // it is an improvement. if (!item2->NoPet) { for (int i = 0; !found && i < EQEmu::legacy::EQUIPMENT_SIZE; i++) { uint32 slots = (1 << i); if (item2->Slots & slots) { if(equipment[i]) { compitem = database.GetItem(equipment[i]); if (item2->AC > compitem->AC || (item2->AC == compitem->AC && item2->HP > compitem->HP)) { // item would be an upgrade // check if we're multi-slot, if yes then we have to keep // looking in case any of the other slots we can fit into are empty. if (item2->Slots != slots) { foundslot = i; } else { equipment[i] = item2->ID; foundslot = i; found = true; } } // end if ac } else { equipment[i] = item2->ID; foundslot = i; found = true; } } // end if (slots) } // end for } // end if NoPet // Possible slot was found but not selected. Pick it now. if (!found && foundslot >= 0) { equipment[foundslot] = item2->ID; found = true; } // @merth: IDFile size has been increased, this needs to change uint16 emat; if(item2->Material <= 0 || item2->Slots & (1 << EQEmu::inventory::slotPrimary | 1 << EQEmu::inventory::slotSecondary)) { memset(newid, 0, sizeof(newid)); for(int i=0;i<7;i++){ if (!isalpha(item2->IDFile[i])){ strn0cpy(newid, &item2->IDFile[i],6); i=8; } } emat = atoi(newid); } else { emat = item2->Material; } if (foundslot == EQEmu::inventory::slotPrimary) { if (item2->Proc.Effect != 0) CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); eslot = EQEmu::textures::weaponPrimary; if (item2->Damage > 0) { SendAddPlayerState(PlayerState::PrimaryWeaponEquipped); SetFacestab(true); } if (item2->IsType2HWeapon()) SetTwoHanderEquipped(true); } else if (foundslot == EQEmu::inventory::slotSecondary && (GetOwner() != nullptr || (CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) && (item2->IsType1HWeapon() || item2->ItemType == EQEmu::item::ItemTypeShield)) { if (item2->Proc.Effect!=0) CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); eslot = EQEmu::textures::weaponSecondary; if (item2->Damage > 0) SendAddPlayerState(PlayerState::SecondaryWeaponEquipped); } else if (foundslot == EQEmu::inventory::slotHead) { eslot = EQEmu::textures::armorHead; } else if (foundslot == EQEmu::inventory::slotChest) { eslot = EQEmu::textures::armorChest; } else if (foundslot == EQEmu::inventory::slotArms) { eslot = EQEmu::textures::armorArms; } else if (foundslot == EQEmu::inventory::slotWrist1 || foundslot == EQEmu::inventory::slotWrist2) { eslot = EQEmu::textures::armorWrist; } else if (foundslot == EQEmu::inventory::slotHands) { eslot = EQEmu::textures::armorHands; } else if (foundslot == EQEmu::inventory::slotLegs) { eslot = EQEmu::textures::armorLegs; } else if (foundslot == EQEmu::inventory::slotFeet) { eslot = EQEmu::textures::armorFeet; } /* what was this about??? if (((npc->GetRace()==127) && (npc->CastToMob()->GetOwnerID()!=0)) && (item2->Slots==24576) || (item2->Slots==8192) || (item2->Slots==16384)){ npc->d_melee_texture2=atoi(newid); wc->wear_slot_id=8; if (item2->Material >0) wc->material=item2->Material; else wc->material=atoi(newid); npc->AC+=item2->AC; npc->STR+=item2->STR; npc->INT+=item2->INT; } */ //if we found an open slot it goes in... if(eslot != 0xFF) { if(wearchange) { wc->wear_slot_id = eslot; wc->material = emat; } } if (found) { CalcBonuses(); // This is less than ideal for bulk adding of items item->equip_slot = foundslot; } } if(itemlist != nullptr) itemlist->push_back(item); else safe_delete(item); if(wearchange && outapp) { entity_list.QueueClients(this, outapp); safe_delete(outapp); } UpdateEquipmentLight(); if (UpdateActiveLight()) SendAppearancePacket(AT_Light, GetActiveLightType()); }
Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard) { uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid); auto buffer = new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))]; PlayerCorpse_Struct *pcs = (PlayerCorpse_Struct*)buffer; database.LoadCharacterCorpseData(in_dbid, pcs); /* Load Items */ ItemList itemlist; ServerLootItem_Struct* tmp = 0; for (unsigned int i = 0; i < pcs->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &pcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); itemlist.push_back(tmp); } /* Create Corpse Entity */ auto pc = new Corpse(in_dbid, // uint32 in_dbid in_charid, // uint32 in_charid in_charname.c_str(), // char* in_charname &itemlist, // ItemList* in_itemlist pcs->copper, // uint32 in_copper pcs->silver, // uint32 in_silver pcs->gold, // uint32 in_gold pcs->plat, // uint32 in_plat position, pcs->size, // float in_size pcs->gender, // uint8 in_gender pcs->race, // uint16 in_race pcs->class_, // uint8 in_class pcs->deity, // uint8 in_deity pcs->level, // uint8 in_level pcs->texture, // uint8 in_texture pcs->helmtexture, // uint8 in_helmtexture pcs->exp, // uint32 in_rezexp was_at_graveyard // bool wasAtGraveyard ); if (pcs->locked) pc->Lock(); /* Load Item Tints */ pc->item_tint.Head.Color = pcs->item_tint.Head.Color; pc->item_tint.Chest.Color = pcs->item_tint.Chest.Color; pc->item_tint.Arms.Color = pcs->item_tint.Arms.Color; pc->item_tint.Wrist.Color = pcs->item_tint.Wrist.Color; pc->item_tint.Hands.Color = pcs->item_tint.Hands.Color; pc->item_tint.Legs.Color = pcs->item_tint.Legs.Color; pc->item_tint.Feet.Color = pcs->item_tint.Feet.Color; pc->item_tint.Primary.Color = pcs->item_tint.Primary.Color; pc->item_tint.Secondary.Color = pcs->item_tint.Secondary.Color; /* Load Physical Appearance */ pc->haircolor = pcs->haircolor; pc->beardcolor = pcs->beardcolor; pc->eyecolor1 = pcs->eyecolor1; pc->eyecolor2 = pcs->eyecolor2; pc->hairstyle = pcs->hairstyle; pc->luclinface = pcs->face; pc->beard = pcs->beard; pc->drakkin_heritage = pcs->drakkin_heritage; pc->drakkin_tattoo = pcs->drakkin_tattoo; pc->drakkin_details = pcs->drakkin_details; pc->IsRezzed(rezzed); pc->become_npc = false; pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values safe_delete_array(pcs); return pc; }
// 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); } #ifdef BOTS else if (this->IsBot()) act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id); #endif } 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 auto 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() #ifdef BOTS || this->IsBot() #endif ) && 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) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 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 = npc_type->size * (1 + (scale_power / 2)) > npc_type->size * 3 ? npc_type->size * 3 : 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()) { safe_delete(npc_type); 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 auto 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[EQEmu::legacy::EQUIPMENT_SIZE]; memset(petinv, 0, sizeof(petinv)); const EQEmu::ItemData *item = 0; if (database.GetBasePetItems(record.equipmentset, petinv)) { for (int i = 0; i < EQEmu::legacy::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) } }