Loading...   


void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* app) {
	if (!client)
		return;

	// Added 12/08. Started compressing loot struct on live.
	if(player_corpse_depop) {
		SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
		return;
	}

	if(IsPlayerCorpse() && !corpse_db_id) { // really should try to resave in this case
		// SendLootReqErrorPacket(client, 0);
		client->Message(13, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!");
		std::cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << std::endl;
		// return;
	}

	if(is_locked && client->Admin() < 100) {
		SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
		client->Message(13, "Error: Corpse locked by GM.");
		return;
	}

	if(!being_looted_by || (being_looted_by != 0xFFFFFFFF && !entity_list.GetID(being_looted_by)))
		being_looted_by = 0xFFFFFFFF;

	if (DistanceSquaredNoZ(client->GetPosition(), m_Position) > 625) {
		SendLootReqErrorPacket(client, LootResponse::TooFar);
		return;
	}
	
	if (being_looted_by != 0xFFFFFFFF && being_looted_by != client->GetID()) {
		SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
		return;
	}

	// all disqualifiers should occur before this point as to not interfere with any current looter
	loot_request_type = LootRequestType::Forbidden;

	// loot_request_type is class scoped and reset on a per-loot session basis
	if (client->GetGM()) {
		if (client->Admin() >= 100)
			loot_request_type = LootRequestType::GMPeek;
	}
	else {
		if (IsPlayerCorpse()) {
			if (char_id == client->CharacterID()) {
				loot_request_type = LootRequestType::Self;
			}
			else if (CanPlayerLoot(client->CharacterID())) {
				if (GetPlayerKillItem() == -1)
					loot_request_type = LootRequestType::AllowedPVPAll;
				else if (GetPlayerKillItem() == 1)
					loot_request_type = LootRequestType::AllowedPVPSingle;
				else if (GetPlayerKillItem() > 1)
					loot_request_type = LootRequestType::AllowedPVPDefined;
			}
		}
		else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) {
			loot_request_type = LootRequestType::AllowedPVE;
		}

	}

	if (loot_request_type == LootRequestType::Forbidden) {
		SendLootReqErrorPacket(client, LootResponse::NotAtThisTime);
		return;
	}

	being_looted_by = client->GetID();
	client->CommonBreakInvisible(); // we should be "all good" so lets break invis now instead of earlier before all error checking is done

	// process coin
	bool loot_coin = false;
	std::string tmp;
	if (database.GetVariable("LootCoin", tmp))
		loot_coin = (tmp[0] == 1 && tmp[1] == '\0');

	if (loot_request_type == LootRequestType::GMPeek) {
		client->Message(15, "This corpse contains %u platinum, %u gold, %u silver and %u copper.");
	}
	else {
		auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct));
		moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer;

		d->response = static_cast<uint8>(LootResponse::Normal);
		d->unknown1 = 0x42;
		d->unknown2 = 0xef;

		Group* cgroup = client->GetGroup();

		// this can be reworked into a switch and massaged to include specialized pve loot rules based on 'LootRequestType'
		if (!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && cgroup) {
			d->copper = 0;
			d->silver = 0;
			d->gold = 0;
			d->platinum = 0;
			cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client);
		}
		else {
			d->copper = GetCopper();
			d->silver = GetSilver();
			d->gold = GetGold();
			d->platinum = GetPlatinum();
			client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), false);
		}

		RemoveCash();
		Save();

		outapp->priority = 6;
		client->QueuePacket(outapp);

		safe_delete(outapp);
	}

	// process items
	auto timestamps = database.GetItemRecastTimestamps(client->CharacterID());

	if (loot_request_type == LootRequestType::AllowedPVPDefined) {
		auto pkitemid = GetPlayerKillItem();
		auto pkitem = database.GetItem(pkitemid);
		auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges);

		if (pkinst) {
			if (pkitem->RecastDelay)
				pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0);

			client->SendItemPacket(EQEmu::invslot::CORPSE_BEGIN, pkinst, ItemPacketLoot);
			safe_delete(pkinst);
		}
		else {
			client->Message(13, "PlayerKillItem (id: %i) could not be found!", pkitemid);
		}

		client->QueuePacket(app);
		return;
	}

	auto loot_slot = EQEmu::invslot::CORPSE_BEGIN;
	auto corpse_mask = EQEmu::inventory::Lookup(EQEmu::versions::ConvertClientVersionToMobVersion(client->ClientVersion()))->CorpseBitmask;

	for (auto item_data : itemlist) {
		item_data->lootslot = 0xFFFF;
		
		if (loot_slot > EQEmu::invslot::CORPSE_END)
			continue;
		
		if (((uint64)1 << loot_slot) & corpse_mask == 0) {
			++loot_slot;
			continue;
		}

		if (IsPlayerCorpse()) {
			if (loot_request_type == LootRequestType::AllowedPVPSingle && loot_slot != EQEmu::invslot::CORPSE_BEGIN)
				continue;

			if (item_data->equip_slot < EQEmu::invslot::POSSESSIONS_BEGIN || item_data->equip_slot > EQEmu::invslot::POSSESSIONS_END)
				continue;
		}
		
		const auto *item = database.GetItem(item_data->item_id);
		auto inst = database.CreateItem(
			item,
			item_data->charges,
			item_data->aug_1,
			item_data->aug_2,
			item_data->aug_3,
			item_data->aug_4,
			item_data->aug_5,
			item_data->aug_6,
			item_data->attuned
		);
		if (!inst)
			continue;

		if (item->RecastDelay)
			inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0);

		client->SendItemPacket(loot_slot, inst, ItemPacketLoot);
		safe_delete(inst);
		
		++loot_slot;
	}

	// Disgrace: Client seems to require that we send the packet back...
	client->QueuePacket(app);

	// This is required for the 'Loot All' feature to work for SoD clients. I expect it is to tell the client that the
	// server has now sent all the items on the corpse.
	if (client->ClientVersion() >= EQEmu::versions::ClientVersion::SoD)
		SendLootReqErrorPacket(client, LootResponse::LootAll);
}

Raw Paste Data