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) {
|
void Bot::RemoveItem(Item* item) {
|
||||||
int8 slot = GetEquipmentList()->GetSlotByItem(item);
|
int8 slot = GetEquipmentList()->GetSlotByItem(item);
|
||||||
if (slot != 255) {
|
if (slot != 255) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ public:
|
||||||
bool IsBot() { return true; }
|
bool IsBot() { return true; }
|
||||||
|
|
||||||
void GiveItem(int32 item_id);
|
void GiveItem(int32 item_id);
|
||||||
|
void GiveItem(Item* item);
|
||||||
void RemoveItem(Item* item);
|
void RemoveItem(Item* item);
|
||||||
void TradeItemAdded(Item* item);
|
void TradeItemAdded(Item* item);
|
||||||
void AddItemToTrade(int8 slot);
|
void AddItemToTrade(int8 slot);
|
||||||
|
|
|
@ -840,6 +840,7 @@ Item::Item(){
|
||||||
generic_info.condition = 100;
|
generic_info.condition = 100;
|
||||||
no_buy_back = false;
|
no_buy_back = false;
|
||||||
no_sale = false;
|
no_sale = false;
|
||||||
|
created = std::time(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::Item(Item* in_item){
|
Item::Item(Item* in_item){
|
||||||
|
@ -857,6 +858,8 @@ Item::Item(Item* in_item){
|
||||||
spell_tier = in_item->spell_tier;
|
spell_tier = in_item->spell_tier;
|
||||||
no_buy_back = in_item->no_buy_back;
|
no_buy_back = in_item->no_buy_back;
|
||||||
no_sale = in_item->no_sale;
|
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(){
|
Item::~Item(){
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define __EQ2_ITEMS__
|
#define __EQ2_ITEMS__
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <ctime>
|
||||||
#include "../../common/types.h"
|
#include "../../common/types.h"
|
||||||
#include "../../common/DataBuffer.h"
|
#include "../../common/DataBuffer.h"
|
||||||
#include "../../common/MiscFunctions.h"
|
#include "../../common/MiscFunctions.h"
|
||||||
|
@ -918,6 +919,10 @@ public:
|
||||||
string item_script;
|
string item_script;
|
||||||
bool no_buy_back;
|
bool no_buy_back;
|
||||||
bool no_sale;
|
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 AddEffect(string effect, int8 percentage, int8 subbulletflag);
|
||||||
void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
|
void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
|
||||||
int32 GetMaxSellValue();
|
int32 GetMaxSellValue();
|
||||||
|
@ -996,7 +1001,6 @@ public:
|
||||||
bool CheckFlag2(int32 flag);
|
bool CheckFlag2(int32 flag);
|
||||||
void AddSlot(int8 slot_id);
|
void AddSlot(int8 slot_id);
|
||||||
void SetSlots(int32 slots);
|
void SetSlots(int32 slots);
|
||||||
bool needs_deletion;
|
|
||||||
};
|
};
|
||||||
class MasterItemList{
|
class MasterItemList{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -27,8 +27,10 @@
|
||||||
#include "../WorldDatabase.h"
|
#include "../WorldDatabase.h"
|
||||||
#include "Items.h"
|
#include "Items.h"
|
||||||
#include "../World.h"
|
#include "../World.h"
|
||||||
|
#include "../Rules/Rules.h"
|
||||||
|
|
||||||
extern World world;
|
extern World world;
|
||||||
|
extern RuleManager rule_manager;
|
||||||
|
|
||||||
// handle new database class til all functions are converted
|
// handle new database class til all functions are converted
|
||||||
void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item)
|
void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item)
|
||||||
|
@ -1055,6 +1057,9 @@ void WorldDatabase::SaveItems(Client* client)
|
||||||
item = item_iter->second;
|
item = item_iter->second;
|
||||||
|
|
||||||
if(item) {
|
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))) {
|
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||||
DeleteItem(client->GetCharacterID(), item, 0);
|
DeleteItem(client->GetCharacterID(), item, 0);
|
||||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||||
|
@ -1080,6 +1085,9 @@ void WorldDatabase::SaveItems(Client* client)
|
||||||
|
|
||||||
if(item)
|
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))) {
|
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||||
DeleteItem(client->GetCharacterID(), item, 0);
|
DeleteItem(client->GetCharacterID(), item, 0);
|
||||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
||||||
|
@ -1107,6 +1115,9 @@ void WorldDatabase::SaveItems(Client* client)
|
||||||
|
|
||||||
if(item)
|
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))) {
|
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||||
DeleteItem(client->GetCharacterID(), item, 0);
|
DeleteItem(client->GetCharacterID(), item, 0);
|
||||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
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++){
|
for (int32 i = 0; i < overflow->size(); i++){
|
||||||
item = overflow->at(i);
|
item = overflow->at(i);
|
||||||
if (item) {
|
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))) {
|
if(item->needs_deletion || (client->IsZoning() && item->CheckFlag(NO_ZONE))) {
|
||||||
DeleteItem(client->GetCharacterID(), item, 0);
|
DeleteItem(client->GetCharacterID(), item, 0);
|
||||||
client->GetPlayer()->item_list.DestroyItem(item->details.index);
|
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)");
|
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,
|
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);
|
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)
|
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)");
|
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);
|
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)
|
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;
|
Query query;
|
||||||
MYSQL_ROW row;
|
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)
|
if(result)
|
||||||
{
|
{
|
||||||
|
@ -1189,9 +1215,8 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
||||||
|
|
||||||
while(result && (row = mysql_fetch_row(result)))
|
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));
|
Item* master_item = master_item_list.GetItem(strtoul(row[3], NULL, 0));
|
||||||
|
|
||||||
if(master_item)
|
if(master_item)
|
||||||
{
|
{
|
||||||
Item* item = new Item(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;
|
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])
|
if(row[4])
|
||||||
item->creator = string(row[4]);//creator
|
item->creator = string(row[4]);//creator
|
||||||
item->adorn0 = atoi(row[5]); //adorn0
|
item->adorn0 = atoi(row[5]); //adorn0
|
||||||
|
@ -1220,6 +1258,21 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
||||||
|
|
||||||
item->generic_info.item_flags += ATTUNED;
|
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.inv_slot_id = atol(row[10]); //bag_id
|
||||||
item->details.count = atoi(row[11]); //count
|
item->details.count = atoi(row[11]); //count
|
||||||
|
@ -1227,6 +1280,8 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
||||||
item->no_sale = (atoul(row[13]) == 1);
|
item->no_sale = (atoul(row[13]) == 1);
|
||||||
item->details.appearance_type = 0;
|
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)
|
if(strncasecmp(row[0], "EQUIPPED", 8)==0)
|
||||||
ret = player->GetEquipmentList()->AddItem(item->details.slot_id, item);
|
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, MinLastNameLength, "4");
|
||||||
RULE_INIT(R_Player, DisableHouseAlignmentRequirement, "1");
|
RULE_INIT(R_Player, DisableHouseAlignmentRequirement, "1");
|
||||||
RULE_INIT(R_Player, MentorItemDecayRate, ".05"); // 5% per level lost when mentoring
|
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 */
|
/* PVP */
|
||||||
RULE_INIT(R_PVP, AllowPVP, "0");
|
RULE_INIT(R_PVP, AllowPVP, "0");
|
||||||
RULE_INIT(R_PVP, LevelRange, "4");
|
RULE_INIT(R_PVP, LevelRange, "4");
|
||||||
|
|
|
@ -72,6 +72,8 @@ enum RuleType {
|
||||||
MinLastNameLength,
|
MinLastNameLength,
|
||||||
DisableHouseAlignmentRequirement,
|
DisableHouseAlignmentRequirement,
|
||||||
MentorItemDecayRate,
|
MentorItemDecayRate,
|
||||||
|
TemporaryItemLogoutTime,
|
||||||
|
HeirloomItemShareExpiration,
|
||||||
|
|
||||||
/* PVP */
|
/* PVP */
|
||||||
AllowPVP,
|
AllowPVP,
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "Bots/Bot.h"
|
#include "Bots/Bot.h"
|
||||||
#include "../common/Log.h"
|
#include "../common/Log.h"
|
||||||
|
#include "Rules/Rules.h"
|
||||||
|
|
||||||
extern ConfigReader configReader;
|
extern ConfigReader configReader;
|
||||||
extern MasterItemList master_item_list;
|
extern MasterItemList master_item_list;
|
||||||
|
extern RuleManager rule_manager;
|
||||||
|
|
||||||
Trade::Trade(Entity* trader1, Entity* trader2) {
|
Trade::Trade(Entity* trader1, Entity* trader2) {
|
||||||
this->trader1 = trader1;
|
this->trader1 = trader1;
|
||||||
|
@ -36,7 +38,7 @@ int8 Trade::AddItemToTrade(Entity* character, Item* item, int8 quantity, int8 sl
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity* other = GetTradee(character);
|
Entity* other = GetTradee(character);
|
||||||
int8 result = CheckItem(character, item, other->IsBot());
|
int8 result = CheckItem(character, item, other);
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
if (character == trader1) {
|
if (character == trader1) {
|
||||||
|
@ -59,11 +61,16 @@ int8 Trade::AddItemToTrade(Entity* character, Item* item, int8 quantity, int8 sl
|
||||||
return result;
|
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;
|
int8 ret = 0;
|
||||||
map<int8, TradeItemInfo>* list = 0;
|
map<int8, TradeItemInfo>* list = 0;
|
||||||
map<int8, TradeItemInfo>::iterator itr;
|
map<int8, TradeItemInfo>::iterator itr;
|
||||||
|
|
||||||
|
bool other_is_bot = false;
|
||||||
|
|
||||||
|
if(other)
|
||||||
|
other_is_bot = other->IsBot();
|
||||||
|
|
||||||
if (trader == trader1)
|
if (trader == trader1)
|
||||||
list = &trader1_items;
|
list = &trader1_items;
|
||||||
else if (trader == trader2)
|
else if (trader == trader2)
|
||||||
|
@ -83,8 +90,17 @@ int8 Trade::CheckItem(Entity* trader, Item* item, bool other_is_bot) {
|
||||||
if (!other_is_bot) {
|
if (!other_is_bot) {
|
||||||
if (item->CheckFlag(NO_TRADE))
|
if (item->CheckFlag(NO_TRADE))
|
||||||
ret = 2;
|
ret = 2;
|
||||||
if (item->CheckFlag2(HEIRLOOM))
|
if (item->CheckFlag2(HEIRLOOM)) {
|
||||||
ret = 3;
|
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() {
|
void Trade::CompleteTrade() {
|
||||||
map<int8, TradeItemInfo>::iterator itr;
|
map<int8, TradeItemInfo>::iterator itr;
|
||||||
map<int32, int8> trader1_item_ids;
|
vector<Item*> trader1_item_pass;
|
||||||
map<int32, int8>::iterator itr2;
|
vector<Item*>::iterator itr2;
|
||||||
string log_string = "TradeComplete:\n";
|
string log_string = "TradeComplete:\n";
|
||||||
|
|
||||||
if (trader1->IsPlayer()) {
|
if (trader1->IsPlayer()) {
|
||||||
|
@ -259,14 +275,19 @@ void Trade::CompleteTrade() {
|
||||||
player->RemoveCoins(trader1_coins);
|
player->RemoveCoins(trader1_coins);
|
||||||
for (itr = trader1_items.begin(); itr != trader1_items.end(); itr++) {
|
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
|
// 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";
|
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);
|
client->RemoveItem(itr->second.item, itr->second.quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
player->AddCoins(trader2_coins);
|
player->AddCoins(trader2_coins);
|
||||||
for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
|
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());
|
PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
|
||||||
|
@ -297,8 +318,8 @@ void Trade::CompleteTrade() {
|
||||||
}
|
}
|
||||||
|
|
||||||
player->AddCoins(trader1_coins);
|
player->AddCoins(trader1_coins);
|
||||||
for (itr2 = trader1_item_ids.begin(); itr2 != trader1_item_ids.end(); itr2++) {
|
for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
|
||||||
client->AddItem(itr2->first, itr2->second);
|
client->AddItem(*itr2, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
|
PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
|
||||||
|
@ -317,9 +338,9 @@ void Trade::CompleteTrade() {
|
||||||
bot->RemoveItem(itr->second.item);
|
bot->RemoveItem(itr->second.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (itr2 = trader1_item_ids.begin(); itr2 != trader1_item_ids.end(); itr2++) {
|
for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
|
||||||
bot->GiveItem(itr2->first);
|
bot->GiveItem(*itr2);
|
||||||
}
|
}
|
||||||
bot->FinishTrade();
|
bot->FinishTrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
bool HasAcceptedTrade(Entity* character);
|
bool HasAcceptedTrade(Entity* character);
|
||||||
void CancelTrade(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:
|
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.HasFreeSlot() || player->item_list.CanStack(item)) {
|
||||||
if (player->item_list.AssignItemToFreeSlot(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;
|
int8 type = CHANNEL_LOOT;
|
||||||
if (entity) {
|
if (entity) {
|
||||||
Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
|
Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
|
||||||
|
|
Loading…
Reference in a new issue