Complete Fix #418 item flags
- Temporary item flag support (removes item 30 minutes from camp out) Rule R_Player, TemporaryItemLogoutTime added for seconds to deletion of item - Heirloom item flag support added (limited to group support) Rule R_Player, HeirloomItemShareExpiration added for seconds to inability to trade item between prior group members(tbd raid) SQL Updates: CREATE TABLE `character_items_group_members` ( `unique_id` int(10) unsigned NOT NULL default 0, `character_id` int(10) unsigned NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=latin1; alter table character_items add column last_saved timestamp default current_timestamp on update current_timestamp; alter table character_items add column created timestamp default current_timestamp;
This commit is contained in:
parent
7ac0861c98
commit
682e023635
13 changed files with 158 additions and 19 deletions
4
DB/character_items_group_members_june30_2022.sql
Normal file
4
DB/character_items_group_members_june30_2022.sql
Normal file
|
@ -0,0 +1,4 @@
|
|||
CREATE TABLE `character_items_group_members` (
|
||||
`unique_id` int(10) unsigned NOT NULL default 0,
|
||||
`character_id` int(10) unsigned NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
1
DB/updates/character_items_updatedcolumn_june30_2022.sql
Normal file
1
DB/updates/character_items_updatedcolumn_june30_2022.sql
Normal file
|
@ -0,0 +1 @@
|
|||
alter table character_items add column last_saved timestamp default current_timestamp on update current_timestamp;
|
1
DB/updates/starting_languages_june_28_2022.sql
Normal file
1
DB/updates/starting_languages_june_28_2022.sql
Normal file
|
@ -0,0 +1 @@
|
|||
update starting_languages set race=2 where race=3 and language_id=3; #dwarf (2) language id of 3 is not erudite race (3)
|
|
@ -57,6 +57,24 @@ void Bot::GiveItem(int32 item_id) {
|
|||
}
|
||||
}
|
||||
|
||||
void Bot::GiveItem(Item* item) {
|
||||
if (item) {
|
||||
int8 slot = GetEquipmentList()->GetFreeSlot(item);
|
||||
if (slot != 255) {
|
||||
GetEquipmentList()->AddItem(slot, item);
|
||||
SetEquipment(item, slot);
|
||||
database.SaveBotItem(BotID, item->details.item_id, slot);
|
||||
if (slot == 0) {
|
||||
ChangePrimaryWeapon();
|
||||
if (IsBot())
|
||||
LogWrite(PLAYER__ERROR, 0, "Bot", "Changing bot primary weapon.");
|
||||
}
|
||||
|
||||
CalculateBonuses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::RemoveItem(Item* item) {
|
||||
int8 slot = GetEquipmentList()->GetSlotByItem(item);
|
||||
if (slot != 255) {
|
||||
|
|
|
@ -15,6 +15,7 @@ public:
|
|||
bool IsBot() { return true; }
|
||||
|
||||
void GiveItem(int32 item_id);
|
||||
void GiveItem(Item* item);
|
||||
void RemoveItem(Item* item);
|
||||
void TradeItemAdded(Item* item);
|
||||
void AddItemToTrade(int8 slot);
|
||||
|
|
|
@ -840,6 +840,7 @@ Item::Item(){
|
|||
generic_info.condition = 100;
|
||||
no_buy_back = false;
|
||||
no_sale = false;
|
||||
created = std::time(nullptr);
|
||||
}
|
||||
|
||||
Item::Item(Item* in_item){
|
||||
|
@ -857,6 +858,8 @@ Item::Item(Item* in_item){
|
|||
spell_tier = in_item->spell_tier;
|
||||
no_buy_back = in_item->no_buy_back;
|
||||
no_sale = in_item->no_sale;
|
||||
created = in_item->created;
|
||||
grouped_char_ids.insert(in_item->grouped_char_ids.begin(), in_item->grouped_char_ids.end());
|
||||
}
|
||||
|
||||
Item::~Item(){
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define __EQ2_ITEMS__
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
#include "../../common/types.h"
|
||||
#include "../../common/DataBuffer.h"
|
||||
#include "../../common/MiscFunctions.h"
|
||||
|
@ -918,6 +919,10 @@ public:
|
|||
string item_script;
|
||||
bool no_buy_back;
|
||||
bool no_sale;
|
||||
bool needs_deletion;
|
||||
std::time_t created;
|
||||
std::map<int32, bool> grouped_char_ids;
|
||||
|
||||
void AddEffect(string effect, int8 percentage, int8 subbulletflag);
|
||||
void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
|
||||
int32 GetMaxSellValue();
|
||||
|
@ -996,7 +1001,6 @@ public:
|
|||
bool CheckFlag2(int32 flag);
|
||||
void AddSlot(int8 slot_id);
|
||||
void SetSlots(int32 slots);
|
||||
bool needs_deletion;
|
||||
};
|
||||
class MasterItemList{
|
||||
public:
|
||||
|
|
|
@ -27,8 +27,10 @@
|
|||
#include "../WorldDatabase.h"
|
||||
#include "Items.h"
|
||||
#include "../World.h"
|
||||
#include "../Rules/Rules.h"
|
||||
|
||||
extern World world;
|
||||
extern RuleManager rule_manager;
|
||||
|
||||
// handle new database class til all functions are converted
|
||||
void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item)
|
||||
|
@ -1055,6 +1057,9 @@ void WorldDatabase::SaveItems(Client* client)
|
|||
item = item_iter->second;
|
||||
|
||||
if(item) {
|
||||
if(item->CheckFlag(TEMPORARY)) {
|
||||
item->save_needed = true; // we need to keep updating the timestamp so it doesn't expire
|
||||
}
|
||||
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||
DeleteItem(client->GetCharacterID(), item, 0);
|
||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||
|
@ -1080,6 +1085,9 @@ void WorldDatabase::SaveItems(Client* client)
|
|||
|
||||
if(item)
|
||||
{
|
||||
if(item->CheckFlag(TEMPORARY)) {
|
||||
item->save_needed = true; // we need to keep updating the timestamp so it doesn't expire
|
||||
}
|
||||
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||
DeleteItem(client->GetCharacterID(), item, 0);
|
||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||
|
@ -1107,6 +1115,9 @@ void WorldDatabase::SaveItems(Client* client)
|
|||
|
||||
if(item)
|
||||
{
|
||||
if(item->CheckFlag(TEMPORARY)) {
|
||||
item->save_needed = true; // we need to keep updating the timestamp so it doesn't expire
|
||||
}
|
||||
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||
DeleteItem(client->GetCharacterID(), item, 0);
|
||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||
|
@ -1126,6 +1137,9 @@ void WorldDatabase::SaveItems(Client* client)
|
|||
for (int32 i = 0; i < overflow->size(); i++){
|
||||
item = overflow->at(i);
|
||||
if (item) {
|
||||
if(item->CheckFlag(TEMPORARY)) {
|
||||
item->save_needed = true; // we need to keep updating the timestamp so it doesn't expire
|
||||
}
|
||||
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||
DeleteItem(client->GetCharacterID(), item, 0);
|
||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||
|
@ -1152,6 +1166,13 @@ void WorldDatabase::SaveItem(int32 account_id, int32 char_id, Item* item, const
|
|||
string update_item = string("REPLACE INTO character_items (id, type, char_id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, no_sale, account_id, login_checksum) VALUES (%u, '%s', %u, %i, %u, '%s', %i, %i, %i, %i, %i, %i, %i, %u, %u, %u, 0)");
|
||||
query.AddQueryAsync(char_id, this, Q_REPLACE, update_item.c_str(), item->details.unique_id, type, char_id, item->details.slot_id, item->details.item_id,
|
||||
getSafeEscapeString(item->creator.c_str()).c_str(),item->adorn0,item->adorn1,item->adorn2, item->generic_info.condition, item->CheckFlag(ATTUNED) ? 1 : 0, item->details.inv_slot_id, item->details.count, item->GetMaxSellValue(), item->no_sale, account_id);
|
||||
if(item->CheckFlag2(HEIRLOOM)) {
|
||||
std::map<int32, bool>::iterator itr;
|
||||
for(itr = item->grouped_char_ids.begin(); itr != item->grouped_char_ids.end(); itr++) {
|
||||
string addmembers_query = string("REPLACE INTO character_items_group_members (unique_id, character_id) VALUES (%u, %u)");
|
||||
query.AddQueryAsync(char_id, this, Q_REPLACE, addmembers_query.c_str(), item->details.unique_id, itr->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldDatabase::DeleteItem(int32 char_id, Item* item, const char* type)
|
||||
|
@ -1173,6 +1194,11 @@ void WorldDatabase::DeleteItem(int32 char_id, Item* item, const char* type)
|
|||
delete_item = string("DELETE FROM character_items WHERE char_id = %u AND (id = %u OR bag_id = %u)");
|
||||
query.RunQuery2(Q_DELETE, delete_item.c_str(), char_id, item->details.unique_id, item->details.unique_id);
|
||||
}
|
||||
|
||||
if(item->CheckFlag2(HEIRLOOM)) {
|
||||
delete_item = string("DELETE FROM character_items_group_members WHERE unique_id = %u");
|
||||
query.RunQuery2(Q_DELETE, delete_item.c_str(), item->details.unique_id);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Player* player, int16 version)
|
||||
|
@ -1181,7 +1207,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, no_sale FROM character_items where char_id = %u or (bag_id = -4 and account_id = %u) ORDER BY slot asc", char_id, account_id);
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, no_sale, UNIX_TIMESTAMP(last_saved), UNIX_TIMESTAMP(created) FROM character_items where char_id = %u or (bag_id = -4 and account_id = %u) ORDER BY slot asc", char_id, account_id);
|
||||
|
||||
if(result)
|
||||
{
|
||||
|
@ -1189,9 +1215,8 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
|
||||
while(result && (row = mysql_fetch_row(result)))
|
||||
{
|
||||
LogWrite(ITEM__DEBUG, 5, "Items", "\tLoading character item: %u, slot: %i", strtoul(row[1], NULL, 0), atoi(row[2]));
|
||||
LogWrite(ITEM__DEBUG, 5, "Items", "Loading character item: %u, slot: %i", strtoul(row[1], NULL, 0), atoi(row[2]));
|
||||
Item* master_item = master_item_list.GetItem(strtoul(row[3], NULL, 0));
|
||||
|
||||
if(master_item)
|
||||
{
|
||||
Item* item = new Item(master_item);
|
||||
|
@ -1203,6 +1228,19 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
|
||||
item->save_needed = false;
|
||||
|
||||
// we need the items basics (unique id slot id bag id) to continue this temporary check
|
||||
if(item->CheckFlag(TEMPORARY)) {
|
||||
std::time_t last_saved = static_cast<std::time_t>(atoul(row[14]));
|
||||
double timeInSeconds = std::difftime(std::time(nullptr), last_saved);
|
||||
LogWrite(ITEM__INFO, 0, "Items", "Character ID %u has a temporary item %s time in seconds %f last saved.", char_id, item->name.c_str(), timeInSeconds);
|
||||
if(timeInSeconds >= rule_manager.GetGlobalRule(R_Player, TemporaryItemLogoutTime)->GetFloat()) {
|
||||
DeleteItem(char_id, item, 0);
|
||||
LogWrite(ITEM__INFO, 0, "Items", "\tCharacter ID %u had a temporary item %s which was removed due to time limit.", char_id, item->name.c_str());
|
||||
safe_delete(item);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(row[4])
|
||||
item->creator = string(row[4]);//creator
|
||||
item->adorn0 = atoi(row[5]); //adorn0
|
||||
|
@ -1221,12 +1259,29 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
item->generic_info.item_flags += ATTUNED;
|
||||
}
|
||||
|
||||
if(item->CheckFlag2(HEIRLOOM)) {
|
||||
MYSQL_ROW row2;
|
||||
MYSQL_RES* result2 = query.RunQuery2(Q_SELECT, "SELECT character_id from character_items_group_members where unique_id = %u", item->details.unique_id);
|
||||
|
||||
if(result2)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
while(result2 && (row2 = mysql_fetch_row(result2)))
|
||||
{
|
||||
item->grouped_char_ids.insert(std::make_pair(atoul(row2[0]),true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item->details.inv_slot_id = atol(row[10]); //bag_id
|
||||
item->details.count = atoi(row[11]); //count
|
||||
item->SetMaxSellValue(atoul(row[12])); //max sell value
|
||||
item->no_sale = (atoul(row[13]) == 1);
|
||||
item->details.appearance_type = 0;
|
||||
|
||||
// position 14 is used for the last_saved timestamp (primarily for checking temporary items on login)
|
||||
item->created = static_cast<std::time_t>(atoul(row[15]));
|
||||
|
||||
if(strncasecmp(row[0], "EQUIPPED", 8)==0)
|
||||
ret = player->GetEquipmentList()->AddItem(item->details.slot_id, item);
|
||||
|
|
|
@ -212,7 +212,8 @@ void RuleManager::Init()
|
|||
RULE_INIT(R_Player, MinLastNameLength, "4");
|
||||
RULE_INIT(R_Player, DisableHouseAlignmentRequirement, "1");
|
||||
RULE_INIT(R_Player, MentorItemDecayRate, ".05"); // 5% per level lost when mentoring
|
||||
|
||||
RULE_INIT(R_Player, TemporaryItemLogoutTime, "1800.0"); // time in seconds (double) for temporary item to decay after being logged out for a period of time, 30 min is the default
|
||||
RULE_INIT(R_Player, HeirloomItemShareExpiration, "172800.0"); // 2 days ('48 hours') in seconds
|
||||
/* PVP */
|
||||
RULE_INIT(R_PVP, AllowPVP, "0");
|
||||
RULE_INIT(R_PVP, LevelRange, "4");
|
||||
|
|
|
@ -72,6 +72,8 @@ enum RuleType {
|
|||
MinLastNameLength,
|
||||
DisableHouseAlignmentRequirement,
|
||||
MentorItemDecayRate,
|
||||
TemporaryItemLogoutTime,
|
||||
HeirloomItemShareExpiration,
|
||||
|
||||
/* PVP */
|
||||
AllowPVP,
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
#include "Entity.h"
|
||||
#include "Bots/Bot.h"
|
||||
#include "../common/Log.h"
|
||||
#include "Rules/Rules.h"
|
||||
|
||||
extern ConfigReader configReader;
|
||||
extern MasterItemList master_item_list;
|
||||
extern RuleManager rule_manager;
|
||||
|
||||
Trade::Trade(Entity* trader1, Entity* trader2) {
|
||||
this->trader1 = trader1;
|
||||
|
@ -36,7 +38,7 @@ int8 Trade::AddItemToTrade(Entity* character, Item* item, int8 quantity, int8 sl
|
|||
}
|
||||
|
||||
Entity* other = GetTradee(character);
|
||||
int8 result = CheckItem(character, item, other->IsBot());
|
||||
int8 result = CheckItem(character, item, other);
|
||||
|
||||
if (result == 0) {
|
||||
if (character == trader1) {
|
||||
|
@ -59,11 +61,16 @@ int8 Trade::AddItemToTrade(Entity* character, Item* item, int8 quantity, int8 sl
|
|||
return result;
|
||||
}
|
||||
|
||||
int8 Trade::CheckItem(Entity* trader, Item* item, bool other_is_bot) {
|
||||
int8 Trade::CheckItem(Entity* trader, Item* item, Entity* other) {
|
||||
int8 ret = 0;
|
||||
map<int8, TradeItemInfo>* list = 0;
|
||||
map<int8, TradeItemInfo>::iterator itr;
|
||||
|
||||
bool other_is_bot = false;
|
||||
|
||||
if(other)
|
||||
other_is_bot = other->IsBot();
|
||||
|
||||
if (trader == trader1)
|
||||
list = &trader1_items;
|
||||
else if (trader == trader2)
|
||||
|
@ -83,8 +90,17 @@ int8 Trade::CheckItem(Entity* trader, Item* item, bool other_is_bot) {
|
|||
if (!other_is_bot) {
|
||||
if (item->CheckFlag(NO_TRADE))
|
||||
ret = 2;
|
||||
if (item->CheckFlag2(HEIRLOOM))
|
||||
ret = 3;
|
||||
if (item->CheckFlag2(HEIRLOOM)) {
|
||||
if(item->grouped_char_ids.find(((Player*)other)->GetCharacterID()) != item->grouped_char_ids.end()) {
|
||||
double diffInSeconds = 0.0; std::difftime(std::time(nullptr), item->created);
|
||||
if(item->CheckFlag(ATTUNED) || ((diffInSeconds = std::difftime(std::time(nullptr), item->created)) && diffInSeconds >= rule_manager.GetGlobalRule(R_Player, HeirloomItemShareExpiration)->GetFloat())) {
|
||||
ret = 3; // denied heirloom cannot be transferred to outside of group after 48 hours (by rule setting) or if already attuned
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = 3; // not part of the group/raid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,8 +259,8 @@ void Trade::Trader2ItemAdd(Item* item, int8 quantity, int8 slot) {
|
|||
|
||||
void Trade::CompleteTrade() {
|
||||
map<int8, TradeItemInfo>::iterator itr;
|
||||
map<int32, int8> trader1_item_ids;
|
||||
map<int32, int8>::iterator itr2;
|
||||
vector<Item*> trader1_item_pass;
|
||||
vector<Item*>::iterator itr2;
|
||||
string log_string = "TradeComplete:\n";
|
||||
|
||||
if (trader1->IsPlayer()) {
|
||||
|
@ -259,14 +275,19 @@ void Trade::CompleteTrade() {
|
|||
player->RemoveCoins(trader1_coins);
|
||||
for (itr = trader1_items.begin(); itr != trader1_items.end(); itr++) {
|
||||
// client->RemoveItem can delete the item so we need to store the item id's and quantity to give to trader2
|
||||
trader1_item_ids[itr->second.item->details.item_id] = itr->second.quantity;
|
||||
Item* newitem = new Item(itr->second.item);
|
||||
newitem->details.count = itr->second.quantity;
|
||||
trader1_item_pass.push_back(newitem);
|
||||
|
||||
log_string += itr->second.item->name + " (" + to_string(itr->second.item->details.item_id) + ") x" + to_string(itr->second.quantity) + "\n";
|
||||
client->RemoveItem(itr->second.item, itr->second.quantity);
|
||||
}
|
||||
|
||||
player->AddCoins(trader2_coins);
|
||||
for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
|
||||
client->AddItem(itr->second.item->details.item_id, itr->second.quantity);
|
||||
Item* newitem = new Item(itr->second.item);
|
||||
newitem->details.count = itr->second.quantity;
|
||||
client->AddItem(newitem, nullptr);
|
||||
}
|
||||
|
||||
PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
|
||||
|
@ -297,8 +318,8 @@ void Trade::CompleteTrade() {
|
|||
}
|
||||
|
||||
player->AddCoins(trader1_coins);
|
||||
for (itr2 = trader1_item_ids.begin(); itr2 != trader1_item_ids.end(); itr2++) {
|
||||
client->AddItem(itr2->first, itr2->second);
|
||||
for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
|
||||
client->AddItem(*itr2, nullptr);
|
||||
}
|
||||
|
||||
PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
|
||||
|
@ -317,8 +338,8 @@ void Trade::CompleteTrade() {
|
|||
bot->RemoveItem(itr->second.item);
|
||||
}
|
||||
|
||||
for (itr2 = trader1_item_ids.begin(); itr2 != trader1_item_ids.end(); itr2++) {
|
||||
bot->GiveItem(itr2->first);
|
||||
for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
|
||||
bot->GiveItem(*itr2);
|
||||
}
|
||||
bot->FinishTrade();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
bool HasAcceptedTrade(Entity* character);
|
||||
void CancelTrade(Entity* character);
|
||||
|
||||
int8 CheckItem(Entity* trader, Item* item, bool other_is_bot);
|
||||
int8 CheckItem(Entity* trader, Item* item, Entity* other);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -2536,6 +2536,34 @@ bool Client::HandleLootItem(Spawn* entity, Item* item) {
|
|||
}
|
||||
if (player->item_list.HasFreeSlot() || player->item_list.CanStack(item)) {
|
||||
if (player->item_list.AssignItemToFreeSlot(item)) {
|
||||
|
||||
if(item->CheckFlag2(HEIRLOOM)) { // TODO: RAID Support
|
||||
GroupMemberInfo* gmi = GetPlayer()->GetGroupMemberInfo();
|
||||
if (gmi && gmi->group_id)
|
||||
{
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
if (group)
|
||||
{
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
if(members) {
|
||||
for (int8 i = 0; i < members->size(); i++) {
|
||||
Entity* member = members->at(i)->member;
|
||||
|
||||
if ((member->GetZone() != this->GetPlayer()->GetZone()))
|
||||
continue;
|
||||
|
||||
if(member->IsPlayer()) {
|
||||
item->grouped_char_ids.insert(std::make_pair(((Player*)member)->GetCharacterID(), true));
|
||||
item->save_needed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8 type = CHANNEL_LOOT;
|
||||
if (entity) {
|
||||
Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
|
||||
|
|
Loading…
Reference in a new issue