/* EQ2Emulator: Everquest II Server Emulator Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) This file is part of EQ2Emulator. EQ2Emulator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. EQ2Emulator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ #include "Loot.h" #include "../client.h" #include "../../common/ConfigReader.h" #include "../classes.h" #include "../../common/debug.h" #include "../zoneserver.h" #include "../Skills.h" #include "../classes.h" #include "../World.h" #include "../LuaInterface.h" #include "../../common/Log.h" #include "../Entity.h" #include "../Rules/Rules.h" extern Classes classes; extern ConfigReader configReader; extern MasterSkillList master_skill_list; extern RuleManager rule_manager; // JA: storing loot-related functions here til I find them all :/ /* void Combat::AddLooter(Entity* victim, Entity* attacker){ MLooters.writelock(__FUNCTION__, __LINE__); if(attacker->IsPlayer()){ PlayerGroup* group = ((Player*)attacker)->GetGroup(); if(group && group->members.size() > 0){ deque::iterator itr; for(itr = group->members.begin(); itr != group->members.end(); itr++) looters[victim].push_back((*itr)->client->GetPlayer()); } else looters[victim].push_back(attacker); } MLooters.releasewritelock(__FUNCTION__, __LINE__); } void Combat::ClearLooters(Entity* spawn){ MLooters.writelock(__FUNCTION__, __LINE__); looters.erase(spawn); MLooters.releasewritelock(__FUNCTION__, __LINE__); } bool Combat::CheckLootAllowed(Entity* dead, Entity* spawn){ if(!spawn || !dead || dead->GetHP() > 0) return false; bool ret = false; MLooters.readlock(__FUNCTION__, __LINE__); if(looters.count(dead) > 0){ vector::iterator itr; for(itr = looters[dead].begin(); itr != looters[dead].end(); itr++){ if(*itr == spawn){ ret = true; break; } } } MLooters.releasereadlock(__FUNCTION__, __LINE__); return ret; } bool Client::HandleLootItem(Entity* entity, Item* item){ if(!item){ SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find item to loot!"); return false; } if(player->item_list.HasFreeSlot() || player->item_list.CanStack(item)){ if(player->item_list.AssignItemToFreeSlot(item)){ int8 type = CHANNEL_COLOR_LOOT; if(GetVersion() >= 973 && GetVersion() <= 1000) type = CHANNEL_COLOR_NEW_LOOT; else if(GetVersion() >= 973) type = CHANNEL_COLOR_NEWEST_LOOT; if(entity) Message(type, "You loot \\aITEM %u 0:%s\\/a from the corpse of %s", item->details.item_id, item->name.c_str(), entity->GetName()); else Message(type, "You found a \\aITEM %u 0:%s\\/a", item->details.item_id, item->name.c_str()); Guild* guild = player->GetGuild(); if (guild && item->details.tier >= ITEM_TAG_LEGENDARY) { char adjective[32]; int8 type; memset(adjective, 0, sizeof(adjective)); if (item->details.tier >= ITEM_TAG_MYTHICAL) { strncpy(adjective, "Mythical", sizeof(adjective) - 1); type = GUILD_EVENT_LOOTS_MYTHICAL_ITEM; } else if (item->details.tier >= ITEM_TAG_FABLED) { strncpy(adjective, "Fabled", sizeof(adjective) - 1); type = GUILD_EVENT_LOOTS_FABELED_ITEM; } else { strncpy(adjective, "Legendary", sizeof(adjective) - 1); type = GUILD_EVENT_LOOTS_LEGENDARY_ITEM; } guild->AddNewGuildEvent(type, "%s has looted the %s \\aITEM %u 0:%s\\/a", Timer::GetUnixTimeStamp(), true, player->GetName(), adjective, item->details.item_id, item->name.c_str()); guild->SendMessageToGuild(type, "%s has looted the %s \\aITEM %u 0:%s\\/a", player->GetName(), adjective, item->details.item_id, item->name.c_str()); } CheckPlayerQuestsItemUpdate(item); return true; } else SimpleMessage(CHANNEL_COLOR_RED, "Could not find free slot to place item."); } else SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to loot item: Inventory is FULL."); return false; } bool Client::HandleLootItem(Entity* entity, int32 item_id){ if(!entity) return false; return HandleLootItem(entity, entity->LootItem(item_id)); } void Client::HandleLoot(EQApplicationPacket* app){ PacketStruct* packet = configReader.getStruct("WS_LootType", GetVersion()); if(packet){ packet->LoadPacketData(app->pBuffer, app->size); int32 loot_id = packet->getType_int32_ByName("loot_id"); bool loot_all = (packet->getType_int8_ByName("loot_all") == 1); safe_delete(packet); int32 item_id = 0; Item* item = 0; Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id); if(player->HasPendingLootItems(loot_id)){ Item* master_item = 0; if(loot_all){ vector* items = player->GetPendingLootItems(loot_id); if(items){ for(int32 i=0;loot_all && isize();i++){ master_item = items->at(i); if(master_item){ item = new Item(master_item); if(item){ loot_all = HandleLootItem(0, item); if(loot_all) player->RemovePendingLootItem(loot_id, item->details.item_id); } } } safe_delete(items); } } else{ packet = configReader.getStruct("WS_LootItem", GetVersion()); if(packet){ packet->LoadPacketData(app->pBuffer, app->size); item_id = packet->getType_int32_ByName("item_id"); vector* items = player->GetPendingLootItems(loot_id); if(items){ for(int32 i=0;isize();i++){ master_item = items->at(i); if(master_item && master_item->details.item_id == item_id){ item = new Item(master_item); if(item && HandleLootItem(0, item)) player->RemovePendingLootItem(loot_id, item->details.item_id); break; } } safe_delete(items); } safe_delete(packet); } } EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion()); if(outapp) QueuePacket(outapp); Loot(0, player->GetPendingLootItems(loot_id), (Entity*)spawn); } else{ if(spawn && spawn->IsEntity() && GetCurrentZone()->GetCombat()->CheckLootAllowed((Entity*)spawn, player)){ if(loot_all){ while(loot_all && ((item_id = ((Entity*)spawn)->GetLootItemID()) > 0)){ loot_all = HandleLootItem((Entity*)spawn, item_id); } } else{ packet = configReader.getStruct("WS_LootItem", GetVersion()); if(packet){ packet->LoadPacketData(app->pBuffer, app->size); item_id = packet->getType_int32_ByName("item_id"); HandleLootItem((Entity*)spawn, item_id); safe_delete(packet); } } EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion()); if(outapp) QueuePacket(outapp); Loot((Entity*)spawn); if(!((Entity*)spawn)->HasLoot()){ CloseLoot(); if(((Entity*)spawn)->IsNPC()) GetCurrentZone()->RemoveDeadSpawn(spawn); } } else{ if(!spawn){ LogWrite(WORLD__ERROR, 0, "World", "Unknown id of %u when looting!", loot_id); SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find spawn to loot!"); } else SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not unable to loot that at this time."); } } } } void Client::SendPendingLoot(int32 total_coins, Entity* entity){ if(entity) Loot(total_coins, player->GetPendingLootItems(entity->GetID()), entity); } void Client::CloseLoot(){ PacketStruct* packet = configReader.getStruct("WS_CloseWindow", GetVersion()); packet->setDataByName("window_id", 4); EQ2Packet* outapp = packet->serialize(); // DumpPacket(outapp); QueuePacket(outapp); safe_delete(packet); } string Client::GetCoinMessage(int32 total_coins){ if(total_coins == 0) return " 0 Copper"; char tmp[64] = {0}; string message = ""; int32 val = 0; if(total_coins >= 1000000){ val = total_coins / 1000000; total_coins -= 1000000 * val; sprintf(tmp, " %u Platinum", val); message.append(tmp); memset(tmp, 0, 64); } if(total_coins >= 10000){ val = total_coins / 10000; total_coins -= 10000 * val; sprintf(tmp, " %u Gold", val); message.append(tmp); memset(tmp, 0, 64); } if(total_coins >= 100){ val = total_coins / 100; total_coins -= 100 * val; sprintf(tmp, " %u Silver", val); message.append(tmp); memset(tmp, 0, 64); } if(total_coins > 0){ sprintf(tmp, " %u Copper", (int32)total_coins); message.append(tmp); } return message; } void Client::Loot(int32 total_coins, vector* items, Entity* entity){ if(!entity){ CloseLoot(); return; } if(total_coins > 0){ player->AddCoins(total_coins); //PlaySound("coin_cha_ching"); string message = ""; if(entity->GetHP() == 0){ message = "You loot "; entity->SetLootCoins(0); } else message = "You receive "; message.append(GetCoinMessage(total_coins)); if(entity->GetHP() == 0) message.append(" from the corpse of ").append(entity->GetName()); int8 type = CHANNEL_COLOR_LOOT; if(GetVersion() >= 973) type = CHANNEL_COLOR_NEW_LOOT; SimpleMessage(type, message.c_str()); } if(!items || items->size() == 0) CloseLoot(); PacketStruct* packet = configReader.getStruct("WS_UpdateLoot", GetVersion()); if(packet){ vector::iterator itr; int32 packet_size = 0; if(items && items->size() > 0){ packet->setDataByName("loot_count", items->size()); packet->setDataByName("display", 1); } packet->setDataByName("unknown2", 1); packet->setDataByName("unknown3", 0x3C); packet->setDataByName("loot_id", entity->GetID()); EQ2Packet* tmpPacket = packet->serialize(); packet_size += tmpPacket->size; uchar* data = 0; if(items && items->size() > 0){ data = new uchar[items->size()*1000 + packet_size]; memset(data, 0, items->size()*1000 + packet_size); } else{ data = new uchar[packet_size]; memset(data, 0, packet_size); } uchar* ptr = data; memcpy(ptr, tmpPacket->pBuffer, tmpPacket->size); ptr += tmpPacket->size; safe_delete(tmpPacket); Item* item = 0; if(items && items->size() > 0){ for(itr = items->begin(); itr != items->end(); itr++){ item = *itr; memcpy(ptr, &item->details.item_id, sizeof(int32)); ptr += sizeof(int32); packet_size += sizeof(int32); tmpPacket = item->serialize(GetVersion(), true, GetPlayer(), false, 1, 0, false, true); if(GetVersion() >= 860){ memcpy(ptr, tmpPacket->pBuffer + 11, tmpPacket->size - 11); ptr += tmpPacket->size - 11; packet_size += tmpPacket->size - 11; } else{ memcpy(ptr, tmpPacket->pBuffer + 10, tmpPacket->size - 10); ptr += tmpPacket->size - 10; packet_size += tmpPacket->size - 10; } safe_delete(tmpPacket); } } packet_size -= sizeof(int32); memcpy(data, &packet_size, sizeof(int32)); packet_size += sizeof(int32); EQ2Packet* outapp = new EQ2Packet(OP_ClientCmdMsg, data, packet_size); //DumpPacket(outapp); QueuePacket(outapp); safe_delete_array(data); safe_delete(packet); } } void Client::Loot(Entity* entity){ if(GetCurrentZone()->GetCombat()->CheckLootAllowed(entity, GetPlayer())){ int32 total_coins = entity->GetLootCoins(); entity->LockLoot(); Loot(total_coins, entity->GetLootItems(), entity); entity->UnlockLoot(); } else SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time."); } bool Player::HasPendingLootItems(int32 id){ return (pending_loot_items.count(id) > 0 && pending_loot_items[id].size() > 0); } bool Player::HasPendingLootItem(int32 id, int32 item_id){ return (pending_loot_items.count(id) > 0 && pending_loot_items[id].count(item_id) > 0); } vector* Player::GetPendingLootItems(int32 id){ vector* ret = 0; if(pending_loot_items.count(id) > 0){ ret = new vector(); map::iterator itr; for(itr = pending_loot_items[id].begin(); itr != pending_loot_items[id].end(); itr++){ if(master_item_list.GetItem(itr->first)) ret->push_back(master_item_list.GetItem(itr->first)); } } return ret; } void Player::RemovePendingLootItem(int32 id, int32 item_id){ if(pending_loot_items.count(id) > 0){ pending_loot_items[id].erase(item_id); if(pending_loot_items[id].size() == 0) pending_loot_items.erase(id); } } void Player::RemovePendingLootItems(int32 id){ if(pending_loot_items.count(id) > 0) pending_loot_items.erase(id); } void Player::AddPendingLootItems(int32 id, vector* items){ if(items){ Item* item = 0; for(int32 i=0;isize();i++){ item = items->at(i); if(item) pending_loot_items[id][item->details.item_id] = true; } } }*/ NPC* Entity::DropChest() { // Check to see if treasure chests are disabled in the rules if (rule_manager.GetGlobalRule(R_World, TreasureChestDisabled)->GetBool()) return 0; NPC* chest = 0; chest = new NPC(); chest->SetAttackable(0); chest->SetShowLevel(0); chest->SetShowName(1); chest->SetTargetable(1); chest->SetLevel(GetLevel()); // Set the brain to a blank brain so it does nothing chest->SetBrain(new BlankBrain(chest)); // Set the x, y, z, heading, location (grid id) to that of the dead spawn chest->SetZone(GetZone()); chest->SetX(GetX()); chest->SetZ(GetZ()); // need to set X/Z BEFORE setting Y chest->SetY(GetY()); // heading needs to be GetHeading() - 180 so the chest faces the proper way chest->SetHeading(GetHeading() - 180); chest->SetLocation(GetLocation()); // Set the primary command to loot and the secondary to disarm chest->AddPrimaryEntityCommand("loot", rule_manager.GetGlobalRule(R_Loot, LootRadius)->GetFloat(), "loot", "", 0, 0); chest->AddSecondaryEntityCommand("Disarm", rule_manager.GetGlobalRule(R_Loot, LootRadius)->GetFloat(), "Disarm", "", 0, 0); // 32 = loot icon for the mouse chest->SetIcon(32); // 1 = show the right click menu chest->SetShowCommandIcon(1); int8 highest_tier = 0; vector::iterator itr; for (itr = ((Spawn*)this)->GetLootItems()->begin(); itr != ((Spawn*)this)->GetLootItems()->end(); ) { if ((*itr)->details.tier >= ITEM_TAG_UNCOMMON) { if ((*itr)->details.tier > highest_tier) highest_tier = (*itr)->details.tier; // Add the item to the chest chest->AddLootItem((*itr)->details.item_id, (*itr)->details.count); // Remove the item from the corpse itr = ((Spawn*)this)->GetLootItems()->erase(itr); } else itr++; } /*4034 = small chest | 5864 = treasure chest | 5865 = ornate treasure chest | 4015 = exquisite chest*/ if (highest_tier >= ITEM_TAG_FABLED) { chest->SetModelType(4015); chest->SetName("Exquisite Chest"); } else if (highest_tier >= ITEM_TAG_LEGENDARY) { chest->SetModelType(5865); chest->SetName("Ornate Chest"); } else if (highest_tier >= ITEM_TAG_TREASURED) { chest->SetModelType(5864); chest->SetName("Treasure Chest"); } else if (highest_tier >= ITEM_TAG_UNCOMMON) { chest->SetModelType(4034); chest->SetName("Small Chest"); } else safe_delete(chest); if (chest) chest->SetShowHandIcon(1); return chest; }