- Fix #504 - Group Options Support (loot methods, yell restrictions, encounter lock features, item rarity, auto split coin, auto loot mode)
- Rule R_Loot, LootDistributionTime added to set lotto/NBG timer countdown for distribution, default 120 (in seconds) - /setautolootmode [x] command now supported, 0 = none, 1 = need/lotto, 2 = decline DB Update: update commands set handler=534 where command='setautolootmode'; - /loot list details added - tracks the loot windows of players and tells if they are still open or closed (to determine when loot should dispense) - Addressed spells causing crashes on deconstruct of NPCs - Fixed inner struct data honoring the IfVariableSet/IfVariableNotSet flag, eg. previously item_id would not honor IfVariableSet/IfVariableNotSet: <Data ElementName="item_count" Type="int8" IfVariableNotSet="loot_all"/> <Data ElementName="item_list" Type="Array" ArraySizeVariable="item_count" IfVariableNotSet="loot_all"> <Data ElementName="item_id" Type="int32" IfVariableNotSet="loot_all"/> </Data>
This commit is contained in:
parent
5c1c66afaf
commit
47196d6b67
25 changed files with 1534 additions and 315 deletions
|
@ -2849,10 +2849,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
if (sep && sep->arg[0]) {
|
||||
if (!spawn->GetDatabaseID())
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Spawn has no database id to assign to loottables.");
|
||||
break;
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "NOTE: Spawn has no database id to assign to loottables.");
|
||||
}
|
||||
else if (!spawn->IsNPC())
|
||||
|
||||
if (!spawn->IsNPC())
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "these /loot list [add/remove/clearall] sub-commands are only designed for an NPC.");
|
||||
break;
|
||||
|
@ -2893,6 +2893,21 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
else
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/loot list clearall - could not match any spawn_id entries in loottable_id.");
|
||||
}
|
||||
else if (!stricmp(sep->arg[0], "details"))
|
||||
{
|
||||
spawn->LockLoot();
|
||||
client->SimpleMessage(CHANNEL_COMMANDS, "Loot Window List:");
|
||||
if (spawn->GetLootWindowList()->size() > 0) {
|
||||
std::map<int32, bool>::iterator itr;
|
||||
for (itr = spawn->GetLootWindowList()->begin(); itr != spawn->GetLootWindowList()->end(); itr++) {
|
||||
Spawn* looter = client->GetPlayer()->GetZone()->GetSpawnByID(itr->first);
|
||||
if (looter) {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Looter: %s IsLootWindowOpen: %s, HasCompletedLootWindow: %s.", looter->GetName(), itr->second ? "NO" : "YES", spawn->HasSpawnLootWindowCompleted(itr->first) ? "YES" : "NO");
|
||||
}
|
||||
}
|
||||
}
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
else
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/loot list argument not supported.");
|
||||
|
@ -2995,7 +3010,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
if (!rule_manager.GetGlobalRule(R_Loot, AutoDisarmChest)->GetBool() && command->handler == COMMAND_DISARM )
|
||||
client->OpenChest(cmdTarget, true);
|
||||
else
|
||||
client->Loot(cmdTarget, rule_manager.GetGlobalRule(R_Loot, AutoDisarmChest)->GetBool());
|
||||
client->LootSpawnRequest(cmdTarget, rule_manager.GetGlobalRule(R_Loot, AutoDisarmChest)->GetBool());
|
||||
if (!(cmdTarget)->HasLoot()){
|
||||
if (((Entity*)cmdTarget)->IsNPC())
|
||||
client->GetCurrentZone()->RemoveDeadSpawn(cmdTarget);
|
||||
|
@ -5708,6 +5723,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
case COMMAND_CUREPLAYER: { Command_CurePlayer(client, sep); break; }
|
||||
case COMMAND_SHARE_QUEST: { Command_ShareQuest(client, sep); break; }
|
||||
case COMMAND_YELL: { Command_Yell(client, sep); break; }
|
||||
case COMMAND_SETAUTOLOOTMODE: { Command_SetAutoLootMode(client, sep); break; }
|
||||
default:
|
||||
{
|
||||
LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str());
|
||||
|
@ -12037,49 +12053,101 @@ void Commands::Command_Yell(Client* client, Seperator* sep) {
|
|||
Spawn* res = nullptr;
|
||||
Spawn* prev = nullptr;
|
||||
bool cycleAll = false;
|
||||
if(player->GetTarget() == player) {
|
||||
if (player->GetTarget() == player) {
|
||||
cycleAll = true; // self target breaks all encounters
|
||||
}
|
||||
else if(player->GetTarget()) {
|
||||
else if (player->GetTarget()) {
|
||||
res = player->GetTarget(); // selected target other than self only dis-engages that encounter
|
||||
}
|
||||
|
||||
if(res && !client->GetPlayer()->IsEngagedBySpawnID(res->GetID()))
|
||||
|
||||
if (res && !client->GetPlayer()->IsEngagedBySpawnID(res->GetID()))
|
||||
return;
|
||||
|
||||
|
||||
bool groupPermissionYell = true;
|
||||
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
// If the player has a group and has a target
|
||||
if (gmi) {
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
if (group && !group->GetGroupOptions()->default_yell && !gmi->leader) { // default_yell_method = 0 means leader only
|
||||
groupPermissionYell = false;
|
||||
}
|
||||
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (!groupPermissionYell) {
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "%s permission to yell denied due to group yell method set to leader only", client->GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
bool alreadyYelled = false;
|
||||
do {
|
||||
if(!res && player->IsEngagedInEncounter(&res)) { // no target is set, dis-engage top of hated by list
|
||||
if (!res && player->IsEngagedInEncounter(&res)) { // no target is set, dis-engage top of hated by list
|
||||
|
||||
}
|
||||
if(!res || prev == res) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(res->IsNPC() && ((NPC*)res)->Brain()) {
|
||||
if(!alreadyYelled) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You yell for help!");
|
||||
string yellMsg = std::string(client->GetPlayer()->GetName()) + " yelled for help!";
|
||||
client->GetPlayer()->GetZone()->SimpleMessage(CHANNEL_COLOR_RED, yellMsg.c_str(), client->GetPlayer(), 35.0f, false);
|
||||
client->GetPlayer()->GetZone()->SendYellPacket(client->GetPlayer());
|
||||
}
|
||||
alreadyYelled = true;
|
||||
|
||||
NPC* npc = (NPC*)res;
|
||||
npc->Brain()->ClearEncounter();
|
||||
npc->SetLockedNoLoot(ENCOUNTER_STATE_BROKEN);
|
||||
npc->UpdateEncounterState(ENCOUNTER_STATE_BROKEN);
|
||||
}
|
||||
prev = res;
|
||||
res = nullptr;
|
||||
}while(cycleAll);
|
||||
|
||||
if(!player->IsEngagedInEncounter()) {
|
||||
if(player->GetInfoStruct()->get_engaged_encounter()) {
|
||||
if (!res || prev == res) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (res->IsNPC() && ((NPC*)res)->Brain()) {
|
||||
if (!alreadyYelled) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You yell for help!");
|
||||
string yellMsg = std::string(client->GetPlayer()->GetName()) + " yelled for help!";
|
||||
client->GetPlayer()->GetZone()->SimpleMessage(CHANNEL_COLOR_RED, yellMsg.c_str(), client->GetPlayer(), 35.0f, false);
|
||||
client->GetPlayer()->GetZone()->SendYellPacket(client->GetPlayer());
|
||||
}
|
||||
alreadyYelled = true;
|
||||
|
||||
NPC* npc = (NPC*)res;
|
||||
npc->Brain()->ClearEncounter();
|
||||
npc->SetLockedNoLoot(ENCOUNTER_STATE_BROKEN);
|
||||
npc->UpdateEncounterState(ENCOUNTER_STATE_BROKEN);
|
||||
}
|
||||
prev = res;
|
||||
res = nullptr;
|
||||
} while (cycleAll);
|
||||
|
||||
if (!player->IsEngagedInEncounter()) {
|
||||
if (player->GetInfoStruct()->get_engaged_encounter()) {
|
||||
player->GetInfoStruct()->set_engaged_encounter(0);
|
||||
player->SetRegenValues((player->GetInfoStruct()->get_effective_level() > 0) ? player->GetInfoStruct()->get_effective_level() : player->GetLevel());
|
||||
client->GetPlayer()->SetCharSheetChanged(true);
|
||||
player->info_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Function: Command_SetAutoLootMode()
|
||||
Purpose : Set player auto loot mode (0 = disabled, 1 = need/lotto, 2 = decline).
|
||||
Example : /setautolootmode [mode]
|
||||
*/
|
||||
void Commands::Command_SetAutoLootMode(Client* client, Seperator* sep) {
|
||||
if (sep && sep->IsNumber(0)) {
|
||||
int8 mode = atoul(sep->arg[0]);
|
||||
switch (mode) {
|
||||
case AutoLootMode::METHOD_DISABLED: {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Disabled auto loot mode");
|
||||
break;
|
||||
}
|
||||
case AutoLootMode::METHOD_ACCEPT: {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Enabled auto loot mode for need and lotto.");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
mode = AutoLootMode::METHOD_DISABLED;
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Enabled auto loot mode to decline need and lotto.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(mode);
|
||||
database.insertCharacterProperty(client, CHAR_PROPERTY_AUTOLOOTMETHOD, (char*)std::to_string(mode).c_str());
|
||||
client->SendDefaultGroupOptions();
|
||||
}
|
||||
}
|
|
@ -449,6 +449,7 @@ public:
|
|||
void Command_CurePlayer(Client* client, Seperator* sep);
|
||||
void Command_ShareQuest(Client* client, Seperator* sep);
|
||||
void Command_Yell(Client* client, Seperator* sep);
|
||||
void Command_SetAutoLootMode(Client* client, Seperator* sep);
|
||||
|
||||
// AA Commands
|
||||
void Get_AA_Xml(Client* client, Seperator* sep);
|
||||
|
@ -937,6 +938,7 @@ private:
|
|||
#define COMMAND_RELOAD_VOICEOVERS 532
|
||||
#define COMMAND_SHARE_QUEST 533
|
||||
|
||||
#define COMMAND_SETAUTOLOOTMODE 534
|
||||
|
||||
#define GET_AA_XML 750
|
||||
#define ADD_AA 751
|
||||
|
|
|
@ -357,6 +357,15 @@ void Entity::MapInfoStruct()
|
|||
|
||||
get_int8_funcs["reload_player_spells"] = l::bind(&InfoStruct::get_reload_player_spells, &info_struct);
|
||||
|
||||
get_int8_funcs["group_loot_method"] = l::bind(&InfoStruct::get_group_loot_method, &info_struct);
|
||||
get_int8_funcs["group_loot_items_rarity"] = l::bind(&InfoStruct::get_group_loot_items_rarity, &info_struct);
|
||||
get_int8_funcs["group_auto_split"] = l::bind(&InfoStruct::get_group_auto_split, &info_struct);
|
||||
get_int8_funcs["group_default_yell"] = l::bind(&InfoStruct::get_group_default_yell, &info_struct);
|
||||
get_int8_funcs["group_autolock"] = l::bind(&InfoStruct::get_group_autolock, &info_struct);
|
||||
get_int8_funcs["group_lock_method"] = l::bind(&InfoStruct::get_group_lock_method, &info_struct);
|
||||
get_int8_funcs["group_solo_autolock"] = l::bind(&InfoStruct::get_group_solo_autolock, &info_struct);
|
||||
get_int8_funcs["group_auto_loot_method"] = l::bind(&InfoStruct::get_group_auto_loot_method, &info_struct);
|
||||
|
||||
get_string_funcs["action_state"] = l::bind(&InfoStruct::get_action_state, &info_struct);
|
||||
get_string_funcs["combat_action_state"] = l::bind(&InfoStruct::get_combat_action_state, &info_struct);
|
||||
|
||||
|
@ -549,6 +558,15 @@ void Entity::MapInfoStruct()
|
|||
|
||||
set_int8_funcs["reload_player_spells"] = l::bind(&InfoStruct::set_reload_player_spells, &info_struct, l::_1);
|
||||
|
||||
set_int8_funcs["group_loot_method"] = l::bind(&InfoStruct::set_group_loot_method, &info_struct, l::_1);
|
||||
set_int8_funcs["group_loot_items_rarity"] = l::bind(&InfoStruct::set_group_loot_items_rarity, &info_struct, l::_1);
|
||||
set_int8_funcs["group_auto_split"] = l::bind(&InfoStruct::set_group_auto_split, &info_struct, l::_1);
|
||||
set_int8_funcs["group_default_yell"] = l::bind(&InfoStruct::set_group_default_yell, &info_struct, l::_1);
|
||||
set_int8_funcs["group_autolock"] = l::bind(&InfoStruct::set_group_autolock, &info_struct, l::_1);
|
||||
set_int8_funcs["group_lock_method"] = l::bind(&InfoStruct::set_group_lock_method, &info_struct, l::_1);
|
||||
set_int8_funcs["group_solo_autolock"] = l::bind(&InfoStruct::set_group_solo_autolock, &info_struct, l::_1);
|
||||
set_int8_funcs["group_auto_loot_method"] = l::bind(&InfoStruct::set_group_auto_loot_method, &info_struct, l::_1);
|
||||
|
||||
set_string_funcs["action_state"] = l::bind(&InfoStruct::set_action_state, &info_struct, l::_1);
|
||||
set_string_funcs["combat_action_state"] = l::bind(&InfoStruct::set_combat_action_state, &info_struct, l::_1);
|
||||
|
||||
|
|
|
@ -279,6 +279,15 @@ struct InfoStruct{
|
|||
first_world_login_ = 0;
|
||||
reload_player_spells_ = 0;
|
||||
|
||||
group_loot_method_ = 1;
|
||||
group_loot_items_rarity_ = 1;
|
||||
group_auto_split_ = 1;
|
||||
group_default_yell_ = 1;
|
||||
group_autolock_ = 0;
|
||||
group_lock_method_ = 0;
|
||||
group_solo_autolock_ = 0;
|
||||
group_auto_loot_method_ = 0;
|
||||
|
||||
action_state_ = std::string("");
|
||||
}
|
||||
|
||||
|
@ -680,6 +689,15 @@ struct InfoStruct{
|
|||
|
||||
int8 get_reload_player_spells() { std::lock_guard<std::mutex> lk(classMutex); return reload_player_spells_; }
|
||||
|
||||
int8 get_group_loot_method() { std::lock_guard<std::mutex> lk(classMutex); return group_loot_method_; }
|
||||
int8 get_group_loot_items_rarity() { std::lock_guard<std::mutex> lk(classMutex); return group_loot_items_rarity_; }
|
||||
int8 get_group_auto_split() { std::lock_guard<std::mutex> lk(classMutex); return group_auto_split_; }
|
||||
int8 get_group_default_yell() { std::lock_guard<std::mutex> lk(classMutex); return group_default_yell_; }
|
||||
int8 get_group_autolock() { std::lock_guard<std::mutex> lk(classMutex); return group_autolock_; }
|
||||
int8 get_group_lock_method() { std::lock_guard<std::mutex> lk(classMutex); return group_lock_method_; }
|
||||
int8 get_group_solo_autolock() { std::lock_guard<std::mutex> lk(classMutex); return group_solo_autolock_; }
|
||||
int8 get_group_auto_loot_method() { std::lock_guard<std::mutex> lk(classMutex); return group_auto_loot_method_; }
|
||||
|
||||
std::string get_action_state() { std::lock_guard<std::mutex> lk(classMutex); return action_state_; }
|
||||
|
||||
std::string get_combat_action_state() { std::lock_guard<std::mutex> lk(classMutex); return combat_action_state_; }
|
||||
|
@ -975,6 +993,15 @@ struct InfoStruct{
|
|||
void set_first_world_login(int8 value) { std::lock_guard<std::mutex> lk(classMutex); first_world_login_ = value; }
|
||||
|
||||
void set_reload_player_spells(int8 value) { std::lock_guard<std::mutex> lk(classMutex); reload_player_spells_ = value; }
|
||||
|
||||
void set_group_loot_method(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_loot_method_ = value; }
|
||||
void set_group_loot_items_rarity(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_loot_items_rarity_ = value; }
|
||||
void set_group_auto_split(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_auto_split_ = value; }
|
||||
void set_group_default_yell(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_default_yell_ = value; }
|
||||
void set_group_autolock(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_autolock_ = value; }
|
||||
void set_group_lock_method(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_lock_method_ = value; }
|
||||
void set_group_solo_autolock(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_solo_autolock_ = value; }
|
||||
void set_group_auto_loot_method(int8 value) { std::lock_guard<std::mutex> lk(classMutex); group_auto_loot_method_ = value; }
|
||||
|
||||
void set_action_state(std::string value) { std::lock_guard<std::mutex> lk(classMutex); action_state_ = value; }
|
||||
|
||||
|
@ -1187,6 +1214,15 @@ private:
|
|||
int8 first_world_login_;
|
||||
int8 reload_player_spells_;
|
||||
|
||||
int8 group_loot_method_;
|
||||
int8 group_loot_items_rarity_;
|
||||
int8 group_auto_split_;
|
||||
int8 group_default_yell_;
|
||||
int8 group_autolock_;
|
||||
int8 group_lock_method_;
|
||||
int8 group_solo_autolock_;
|
||||
int8 group_auto_loot_method_;
|
||||
|
||||
std::string action_state_;
|
||||
std::string combat_action_state_;
|
||||
|
||||
|
|
|
@ -2304,11 +2304,7 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
int8 max_slots = player->GetMaxBagSlots(client->GetVersion());
|
||||
if (bag_info->num_slots > max_slots)
|
||||
bag_info->num_slots = max_slots;
|
||||
if (client->GetVersion() <= 546) {
|
||||
packet->setSubstructDataByName("details", "num_slots", bag_info->num_slots);
|
||||
packet->setSubstructDataByName("details", "weight_reduction", bag_info->weight_reduction);
|
||||
}
|
||||
else {
|
||||
|
||||
int16 free_slots = bag_info->num_slots;
|
||||
if (player) {
|
||||
Item* bag = player->GetPlayerItemList()->GetItemFromUniqueID(details.unique_id, true);
|
||||
|
@ -2354,7 +2350,6 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
int8 blah[] = { 0xd8,0x66,0x9b,0x6d,0xb6,0xfb,0x7f };
|
||||
for (int8 i = 0; i < sizeof(blah); i++)
|
||||
packet->setSubstructDataByName("footer", "footer_unknown_0", blah[i], 0, i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,8 @@ NPC* Entity::DropChest() {
|
|||
chest->SetIcon(32);
|
||||
// 1 = show the right click menu
|
||||
chest->SetShowCommandIcon(1);
|
||||
|
||||
chest->SetLootMethod(this->GetLootMethod(), this->GetLootRarity(), this->GetLootGroupID());
|
||||
chest->SetLootName(this->GetName());
|
||||
int8 highest_tier = 0;
|
||||
vector<Item*>::iterator itr;
|
||||
for (itr = ((Spawn*)this)->GetLootItems()->begin(); itr != ((Spawn*)this)->GetLootItems()->end(); ) {
|
||||
|
|
|
@ -11152,7 +11152,7 @@ int EQ2Emu_lua_GetSpellTier(lua_State* state) {
|
|||
|
||||
LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (!luaspell) {
|
||||
if (!luaspell || !luaspell->spell) {
|
||||
lua_interface->LogError("%s: LUA GetSpellTier command error: must be used in a spell script", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
}
|
||||
|
@ -11168,7 +11168,7 @@ int EQ2Emu_lua_GetSpellID(lua_State* state) {
|
|||
|
||||
LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (!luaspell) {
|
||||
if (!luaspell || !luaspell->spell) {
|
||||
lua_interface->LogError("%s: LUA GetSpellID command error: must be used in a spell script", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
}
|
||||
|
@ -11321,7 +11321,7 @@ int EQ2Emu_lua_ShowLootWindow(lua_State* state) {
|
|||
lua_interface->LogError("%s: LUA ShowLootWindow has no items", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
}
|
||||
client->Loot(spawn->GetLootCoins(), items, spawn);
|
||||
client->SendLootResponsePacket(spawn->GetLootCoins(), items, spawn, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -575,14 +575,24 @@ bool Brain::CheckLootAllowed(Entity* entity) {
|
|||
bool ret = false;
|
||||
vector<int32>::iterator itr;
|
||||
|
||||
if(m_body)
|
||||
if (m_body)
|
||||
{
|
||||
if(rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByDropTime)->GetInt8()
|
||||
&& m_body->GetChestDropTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime()+(rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeDrop)->GetInt32()*1000))
|
||||
if ((m_body->GetLootMethod() != GroupLootMethod::METHOD_LOTTO && m_body->GetLootMethod() != GroupLootMethod::METHOD_NEED_BEFORE_GREED) && m_body->GetLooterSpawnID() > 0 && m_body->GetLooterSpawnID() != entity->GetID()) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: CheckLootAllowed failed, looter spawn id %u does not match received %s(%u)", GetBody()->GetName(), m_body->GetLooterSpawnID(), entity->GetName(), entity->GetID());
|
||||
return false;
|
||||
}
|
||||
if (rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByDropTime)->GetInt8()
|
||||
&& m_body->GetChestDropTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime() + (rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeDrop)->GetInt32() * 1000)) {
|
||||
return true;
|
||||
if(rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByTrapTime)->GetInt8()
|
||||
&& m_body->GetTrapOpenedTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime()+(rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeTrap)->GetInt32()*1000))
|
||||
}
|
||||
if (rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByTrapTime)->GetInt8()
|
||||
&& m_body->GetTrapOpenedTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime() + (rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeTrap)->GetInt32() * 1000)) {
|
||||
return true;
|
||||
}
|
||||
if ((m_body->GetLootMethod() == GroupLootMethod::METHOD_LOTTO || m_body->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED) && m_body->HasSpawnLootWindowCompleted(entity->GetID())) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: CheckLootAllowed failed, looter %s(%u) has already completed their lotto selections.", GetBody()->GetName(), entity->GetName(), entity->GetID());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check the encounter list to see if the given entity is in it, if so return true.
|
||||
MEncounter.readlock(__FUNCTION__, __LINE__);
|
||||
|
@ -608,7 +618,7 @@ bool Brain::CheckLootAllowed(Entity* entity) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
|
||||
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -125,7 +125,6 @@ Player::Player(){
|
|||
}
|
||||
Player::~Player(){
|
||||
SetSaveSpellEffects(true);
|
||||
DeleteSpellEffects();
|
||||
for(int32 i=0;i<spells.size();i++){
|
||||
safe_delete(spells[i]);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ extern RuleManager rule_manager;
|
|||
PlayerGroup::PlayerGroup(int32 id) {
|
||||
m_id = id;
|
||||
MGroupMembers.SetName("MGroupMembers");
|
||||
SetDefaultGroupOptions();
|
||||
}
|
||||
|
||||
PlayerGroup::~PlayerGroup() {
|
||||
|
@ -163,6 +164,24 @@ void PlayerGroup::SimpleGroupMessage(const char* message) {
|
|||
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void PlayerGroup::SendGroupMessage(int8 type, const char* message, ...) {
|
||||
va_list argptr;
|
||||
char buffer[4096];
|
||||
buffer[0] = 0;
|
||||
va_start(argptr, message);
|
||||
vsnprintf(buffer, sizeof(buffer), message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
|
||||
GroupMemberInfo* info = *itr;
|
||||
if (info->client)
|
||||
info->client->SimpleMessage(type, buffer);
|
||||
}
|
||||
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void PlayerGroup::GroupChatMessage(Spawn* from, int32 language, const char* message) {
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
|
@ -192,7 +211,6 @@ bool PlayerGroup::MakeLeader(Entity* new_leader) {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PlayerGroup::ShareQuestWithGroup(Client* quest_sharer, Quest* quest) {
|
||||
if(!quest || !quest_sharer)
|
||||
return false;
|
||||
|
@ -311,12 +329,23 @@ void PlayerGroupManager::NewGroup(Entity* leader) {
|
|||
// Create a new group with the valid ID we got from above
|
||||
PlayerGroup* new_group = new PlayerGroup(m_nextGroupID);
|
||||
|
||||
GroupOptions goptions;
|
||||
goptions.loot_method = leader->GetInfoStruct()->get_group_loot_method();
|
||||
goptions.loot_items_rarity = leader->GetInfoStruct()->get_group_loot_items_rarity();
|
||||
goptions.auto_split = leader->GetInfoStruct()->get_group_auto_split();
|
||||
goptions.default_yell = leader->GetInfoStruct()->get_group_default_yell();
|
||||
goptions.group_autolock = leader->GetInfoStruct()->get_group_autolock();
|
||||
goptions.group_lock_method = leader->GetInfoStruct()->get_group_lock_method();
|
||||
goptions.solo_autolock = leader->GetInfoStruct()->get_group_solo_autolock();
|
||||
goptions.auto_loot_method = leader->GetInfoStruct()->get_group_auto_loot_method();
|
||||
new_group->SetDefaultGroupOptions(&goptions);
|
||||
|
||||
// Add the new group to the list (need to do this first, AddMember needs ref to the PlayerGroup ptr -> UpdateGroupMemberInfo)
|
||||
m_groups[m_nextGroupID] = new_group;
|
||||
|
||||
// Add the leader to the group
|
||||
new_group->AddMember(leader);
|
||||
|
||||
|
||||
leader->GetGroupMemberInfo()->leader = true;
|
||||
}
|
||||
|
||||
|
@ -599,6 +628,20 @@ void PlayerGroupManager::SimpleGroupMessage(int32 group_id, const char* message)
|
|||
m_groups[group_id]->SimpleGroupMessage(message);
|
||||
}
|
||||
|
||||
void PlayerGroupManager::SendGroupMessage(int32 group_id, int8 type, const char* message, ...) {
|
||||
std::shared_lock lock(MGroups);
|
||||
|
||||
va_list argptr;
|
||||
char buffer[4096];
|
||||
buffer[0] = 0;
|
||||
va_start(argptr, message);
|
||||
vsnprintf(buffer, sizeof(buffer), message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
if (m_groups.count(group_id) > 0)
|
||||
m_groups[group_id]->SendGroupMessage(type, buffer);
|
||||
}
|
||||
|
||||
void PlayerGroupManager::GroupMessage(int32 group_id, const char* message, ...) {
|
||||
va_list argptr;
|
||||
char buffer[4096];
|
||||
|
@ -928,3 +971,30 @@ Entity* PlayerGroup::GetGroupMemberByPosition(Entity* seeker, int32 mapped_posit
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PlayerGroup::SetDefaultGroupOptions(GroupOptions* options) {
|
||||
MGroupMembers.writelock();
|
||||
if (options != nullptr) {
|
||||
group_options.loot_method = options->loot_method;
|
||||
group_options.loot_items_rarity = options->loot_items_rarity;
|
||||
group_options.auto_split = options->auto_split;
|
||||
group_options.default_yell = options->default_yell;
|
||||
group_options.group_lock_method = options->group_lock_method;
|
||||
group_options.group_autolock = options->group_autolock;
|
||||
group_options.solo_autolock = options->solo_autolock;
|
||||
group_options.auto_loot_method = options->auto_loot_method;
|
||||
}
|
||||
else {
|
||||
group_options.loot_method = 1;
|
||||
group_options.loot_items_rarity = 0;
|
||||
group_options.auto_split = 1;
|
||||
group_options.default_yell = 1;
|
||||
group_options.group_lock_method = 0;
|
||||
group_options.group_autolock = 0;
|
||||
group_options.solo_autolock = 0;
|
||||
group_options.auto_loot_method = 0;
|
||||
group_options.last_looted_index = 0;
|
||||
}
|
||||
|
||||
MGroupMembers.releasewritelock();
|
||||
}
|
||||
|
|
|
@ -38,8 +38,11 @@ struct GroupOptions{
|
|||
int8 loot_items_rarity;
|
||||
int8 auto_split;
|
||||
int8 default_yell;
|
||||
int8 group_lock_method;
|
||||
int8 group_autolock;
|
||||
int8 solo_autolock;
|
||||
int8 auto_loot_method;
|
||||
int8 last_looted_index;
|
||||
};
|
||||
|
||||
/// <summary>All the generic info for the group window, plus a client pointer for players</summary>
|
||||
|
@ -94,6 +97,7 @@ public:
|
|||
|
||||
|
||||
void SimpleGroupMessage(const char* message);
|
||||
void SendGroupMessage(int8 type, const char* message, ...);
|
||||
void GroupChatMessage(Spawn* from, int32 language, const char* message);
|
||||
bool MakeLeader(Entity* new_leader);
|
||||
|
||||
|
@ -103,8 +107,14 @@ public:
|
|||
void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked=false);
|
||||
Entity* GetGroupMemberByPosition(Entity* seeker, int32 mapped_position);
|
||||
|
||||
void SetDefaultGroupOptions(GroupOptions* options=nullptr);
|
||||
|
||||
GroupOptions* GetGroupOptions() { return &group_options; }
|
||||
int8 GetLastLooterIndex() { return group_options.last_looted_index; }
|
||||
void SetNextLooterIndex(int8 new_index) { group_options.last_looted_index = new_index; }
|
||||
Mutex MGroupMembers; // Mutex for the group members
|
||||
private:
|
||||
GroupOptions group_options;
|
||||
int32 m_id; // ID of this group
|
||||
deque<GroupMemberInfo*> m_members; // List of members in this group
|
||||
|
||||
|
@ -186,6 +196,7 @@ public:
|
|||
bool HasGroupCompletedQuest(int32 group_id, int32 quest_id);
|
||||
|
||||
void SimpleGroupMessage(int32 group_id, const char* message);
|
||||
void SendGroupMessage(int32 group_id, int8 type, const char* message, ...);
|
||||
void GroupMessage(int32 group_id, const char* message, ...);
|
||||
void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message);
|
||||
bool MakeLeader(int32 group_id, Entity* new_leader);
|
||||
|
|
|
@ -141,6 +141,12 @@ Spawn::Spawn(){
|
|||
scared_by_strong_players = false;
|
||||
is_alive = true;
|
||||
SetLockedNoLoot(ENCOUNTER_STATE_AVAILABLE);
|
||||
loot_method = GroupLootMethod::METHOD_FFA;
|
||||
loot_rarity = 0;
|
||||
loot_group_id = 0;
|
||||
looter_spawn_id = 0;
|
||||
is_loot_complete = false;
|
||||
is_loot_dispensed = false;
|
||||
}
|
||||
|
||||
Spawn::~Spawn(){
|
||||
|
@ -3707,33 +3713,81 @@ void Spawn::UpdateEncounterState(int8 new_state) {
|
|||
}
|
||||
}
|
||||
|
||||
void Spawn::CheckEncounterState(Entity* victim) {
|
||||
if(!IsEntity() || !victim->IsNPC())
|
||||
void Spawn::CheckEncounterState(Entity* victim, bool test_auto_lock) {
|
||||
if (!IsEntity() || !victim->IsNPC())
|
||||
return;
|
||||
|
||||
|
||||
Entity* ent = ((Entity*)this);
|
||||
if(victim->GetLockedNoLoot() == ENCOUNTER_STATE_AVAILABLE) {
|
||||
if(!ent->GetInfoStruct()->get_engaged_encounter()) {
|
||||
ent->GetInfoStruct()->set_engaged_encounter(1);
|
||||
}
|
||||
|
||||
if (victim->GetLockedNoLoot() == ENCOUNTER_STATE_AVAILABLE) {
|
||||
|
||||
Entity* attacker = nullptr;
|
||||
if(ent->GetOwner())
|
||||
if (ent->GetOwner())
|
||||
attacker = ent->GetOwner();
|
||||
else
|
||||
attacker = ent;
|
||||
|
||||
if(!attacker->GetInfoStruct()->get_engaged_encounter()) {
|
||||
|
||||
bool matchedAutoLock = false;
|
||||
if (attacker->IsEntity() && ((Entity*)attacker)->GetGroupMemberInfo()) {
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
GroupMemberInfo* gmi = ((Entity*)attacker)->GetGroupMemberInfo();
|
||||
if (gmi && gmi->group_id)
|
||||
{
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
if (group && ((group->GetGroupOptions()->group_lock_method && group->GetGroupOptions()->group_autolock == 1) || attacker->GetGroupMemberInfo()->leader))
|
||||
{
|
||||
matchedAutoLock = true;
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
|
||||
for (int8 i = 0; i < members->size(); i++) {
|
||||
Entity* member = members->at(i)->member;
|
||||
if (member->GetZone() != attacker->GetZone())
|
||||
continue;
|
||||
|
||||
if (member->IsEntity()) {
|
||||
if (!member->GetInfoStruct()->get_engaged_encounter()) {
|
||||
member->GetInfoStruct()->set_engaged_encounter(1);
|
||||
}
|
||||
if (((NPC*)victim)->Brain()) {
|
||||
((NPC*)victim)->Brain()->AddHate(member, 0);
|
||||
((NPC*)victim)->Brain()->AddToEncounter(member);
|
||||
victim->AddTargetToEncounter(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
else if (attacker->GetInfoStruct()->get_group_solo_autolock()) {
|
||||
matchedAutoLock = true;
|
||||
if (((NPC*)victim)->Brain()) {
|
||||
((NPC*)victim)->Brain()->AddHate(attacker, 0);
|
||||
((NPC*)victim)->Brain()->AddToEncounter(attacker);
|
||||
victim->AddTargetToEncounter(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
if (test_auto_lock && !matchedAutoLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ent->GetInfoStruct()->get_engaged_encounter()) {
|
||||
ent->GetInfoStruct()->set_engaged_encounter(1);
|
||||
}
|
||||
|
||||
if (!attacker->GetInfoStruct()->get_engaged_encounter()) {
|
||||
attacker->GetInfoStruct()->set_engaged_encounter(1);
|
||||
}
|
||||
|
||||
|
||||
int8 skip_loot_gray_mob_flag = rule_manager.GetGlobalRule(R_Loot, SkipLootGrayMob)->GetInt8();
|
||||
|
||||
|
||||
int8 difficulty = attacker->GetArrowColor(victim->GetLevel());
|
||||
|
||||
|
||||
int8 new_enc_state = ENCOUNTER_STATE_AVAILABLE;
|
||||
if(skip_loot_gray_mob_flag && difficulty == ARROW_COLOR_GRAY) {
|
||||
if(!attacker->IsPlayer() && !attacker->IsBot()) {
|
||||
if (skip_loot_gray_mob_flag && difficulty == ARROW_COLOR_GRAY) {
|
||||
if (!attacker->IsPlayer() && !attacker->IsBot()) {
|
||||
new_enc_state = ENCOUNTER_STATE_BROKEN;
|
||||
}
|
||||
else {
|
||||
|
@ -3741,14 +3795,14 @@ void Spawn::CheckEncounterState(Entity* victim) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if(attacker->IsPlayer() || attacker->IsBot()) {
|
||||
if (attacker->IsPlayer() || attacker->IsBot()) {
|
||||
new_enc_state = ENCOUNTER_STATE_LOCKED;
|
||||
}
|
||||
else {
|
||||
new_enc_state = ENCOUNTER_STATE_BROKEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
victim->SetLockedNoLoot(new_enc_state);
|
||||
victim->UpdateEncounterState(new_enc_state);
|
||||
}
|
||||
|
@ -4210,6 +4264,16 @@ int32 Spawn::GetLootItemID() {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void Spawn::GetLootItemsList(std::vector<int32>* out_entries) {
|
||||
if(!out_entries)
|
||||
return;
|
||||
|
||||
vector<Item*>::iterator itr;
|
||||
for (itr = loot_items.begin(); itr != loot_items.end(); itr++) {
|
||||
out_entries->push_back((*itr)->details.item_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Spawn::HasLootItemID(int32 id) {
|
||||
bool ret = false;
|
||||
|
||||
|
@ -4702,4 +4766,264 @@ void Spawn::SendGroupUpdate() {
|
|||
else
|
||||
world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Spawn::AddNeedGreedItemRequest(int32 item_id, int32 spawn_id, bool need_item) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: AddNeedGreedItemRequest Item ID: %u, Spawn ID: %u, Need Item: %u", GetName(), item_id, spawn_id, need_item);
|
||||
if (HasSpawnNeedGreedEntry(item_id, spawn_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
need_greed_items.insert(make_pair(item_id, std::make_pair(spawn_id, need_item)));
|
||||
|
||||
AddSpawnLootWindowCompleted(spawn_id, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Spawn::AddLottoItemRequest(int32 item_id, int32 spawn_id) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: AddLottoItemRequest Item ID: %u, Spawn ID: %u", GetName(), item_id, spawn_id);
|
||||
if (HasSpawnLottoEntry(item_id, spawn_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lotto_items.insert(make_pair(item_id, spawn_id));
|
||||
|
||||
AddSpawnLootWindowCompleted(spawn_id, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Spawn::AddSpawnLootWindowCompleted(int32 spawn_id, bool status_) {
|
||||
if (loot_complete.find(spawn_id) == loot_complete.end()) {
|
||||
loot_complete.insert(make_pair(spawn_id, status_));
|
||||
}
|
||||
|
||||
is_loot_complete = HasLootWindowCompleted();
|
||||
}
|
||||
|
||||
bool Spawn::SetSpawnLootWindowCompleted(int32 spawn_id) {
|
||||
std::map<int32, bool>::iterator itr = loot_complete.find(spawn_id);
|
||||
if (itr != loot_complete.end()) {
|
||||
itr->second = true;
|
||||
is_loot_complete = HasLootWindowCompleted();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spawn::HasSpawnLootWindowCompleted(int32 spawn_id) {
|
||||
std::map<int32, bool>::iterator itr = loot_complete.find(spawn_id);
|
||||
if (itr != loot_complete.end() && itr->second) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spawn::HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id) {
|
||||
for (auto [itr, rangeEnd] = need_greed_items.equal_range(item_id); itr != rangeEnd; itr++) {
|
||||
LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second.first);
|
||||
if (spawn_id == itr->second.first) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spawn::HasSpawnLottoEntry(int32 item_id, int32 spawn_id) {
|
||||
for (auto [itr, rangeEnd] = lotto_items.equal_range(item_id); itr != rangeEnd; itr++) {
|
||||
LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second);
|
||||
if (spawn_id == itr->second) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Spawn::GetSpawnLottoEntries(int32 item_id, std::map<int32, int32>* out_entries) {
|
||||
if (!out_entries)
|
||||
return;
|
||||
|
||||
std::map<int32, bool> spawn_matches;
|
||||
for (auto [itr, endrange] = lotto_items.equal_range(item_id); itr != endrange; itr++) {
|
||||
out_entries->insert(std::make_pair(itr->second, (int32)MakeRandomInt(0, 100)));
|
||||
spawn_matches[itr->second] = true;
|
||||
}
|
||||
|
||||
// 0xFFFFFFFF represents selecting "All" on the lotto screen
|
||||
for (auto [itr, endrange] = lotto_items.equal_range(0xFFFFFFFF); itr != endrange; itr++) {
|
||||
if (spawn_matches.find(itr->second) == spawn_matches.end()) {
|
||||
out_entries->insert(std::make_pair(itr->second, (int32)MakeRandomInt(0, 100)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spawn::GetSpawnNeedGreedEntries(int32 item_id, bool need_item, std::map<int32, int32>* out_entries) {
|
||||
if (!out_entries)
|
||||
return;
|
||||
|
||||
for (auto [itr, rangeEnd] = need_greed_items.equal_range(item_id); itr != rangeEnd; itr++) {
|
||||
out_entries->insert(std::make_pair(itr->second.first, (int32)MakeRandomInt(0, 100)));
|
||||
}
|
||||
}
|
||||
|
||||
bool Spawn::HasLootWindowCompleted() {
|
||||
std::map<int32, bool>::iterator itr;
|
||||
for (itr = loot_complete.begin(); itr != loot_complete.end(); itr++) {
|
||||
if (!itr->second)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Spawn::StartLootTimer(Spawn* looter) {
|
||||
if (!IsLootTimerRunning()) {
|
||||
int32 loot_timer_time = rule_manager.GetGlobalRule(R_Loot, LootDistributionTime)->GetInt32() * 1000;
|
||||
if(rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByDropTime)->GetBool() && loot_timer_time > rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeDrop)->GetInt32()*1000) {
|
||||
loot_timer_time = (rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeDrop)->GetInt32()*1000) / 2;
|
||||
}
|
||||
|
||||
if(rule_manager.GetGlobalRule(R_Loot, AllowChestUnlockByTrapTime)->GetBool() && loot_timer_time > rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeTrap)->GetInt32()*1000) {
|
||||
loot_timer_time = (rule_manager.GetGlobalRule(R_Loot, ChestUnlockedTimeTrap)->GetInt32()*1000) / 2;
|
||||
}
|
||||
|
||||
if(loot_timer_time < 1000) {
|
||||
loot_timer_time = 60000; // hardcode assure they aren't setting some really ridiculous low number
|
||||
}
|
||||
|
||||
loot_timer.Start(loot_timer_time, true);
|
||||
}
|
||||
if (looter) {
|
||||
looter_spawn_id = looter->GetID();
|
||||
}
|
||||
}
|
||||
|
||||
void Spawn::CloseLoot(Spawn* sender) {
|
||||
if (sender) {
|
||||
SetSpawnLootWindowCompleted(sender->GetID());
|
||||
}
|
||||
if (sender && looter_spawn_id > 0 && sender->GetID() != looter_spawn_id) {
|
||||
LogWrite(LOOT__ERROR, 0, "Loot", "%s: CloseLoot Looter Spawn ID: %u does not match sender %u.", GetName(), looter_spawn_id, sender->GetID());
|
||||
return;
|
||||
}
|
||||
if (!IsLootTimerRunning() && GetLootMethod() != GroupLootMethod::METHOD_LOTTO && GetLootMethod() != GroupLootMethod::METHOD_NEED_BEFORE_GREED) {
|
||||
loot_timer.Disable();
|
||||
}
|
||||
looter_spawn_id = 0;
|
||||
}
|
||||
|
||||
void Spawn::SetLootMethod(GroupLootMethod method, int8 item_rarity, int32 group_id) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: Set Loot Method : %u, group id : %u", GetName(), (int32)method, group_id);
|
||||
loot_group_id = group_id;
|
||||
loot_method = method;
|
||||
loot_rarity = item_rarity;
|
||||
if (loot_name.size() < 1) {
|
||||
loot_name = std::string(GetName());
|
||||
}
|
||||
}
|
||||
|
||||
bool Spawn::IsItemInLootTier(Item* item) {
|
||||
if (!item)
|
||||
return true;
|
||||
|
||||
bool skipItem = true;
|
||||
switch (GetLootRarity()) {
|
||||
case LootTier::ITEMS_TREASURED_PLUS: {
|
||||
if (item->details.tier >= ITEM_TAG_TREASURED) {
|
||||
skipItem = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LootTier::ITEMS_LEGENDARY_PLUS: {
|
||||
if (item->details.tier >= ITEM_TAG_LEGENDARY) {
|
||||
skipItem = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LootTier::ITEMS_FABLED_PLUS: {
|
||||
if (item->details.tier >= ITEM_TAG_FABLED) {
|
||||
skipItem = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
skipItem = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return skipItem;
|
||||
}
|
||||
|
||||
void Spawn::DistributeGroupLoot_RoundRobin(std::vector<int32>* item_list, bool roundRobinTrashLoot) {
|
||||
|
||||
std::vector<int32>::iterator item_itr;
|
||||
|
||||
for (item_itr = item_list->begin(); item_itr != item_list->end(); item_itr++) {
|
||||
int32 item_id = *item_itr;
|
||||
Item* tmpItem = master_item_list.GetItem(item_id);
|
||||
Spawn* looter = nullptr;
|
||||
|
||||
bool skipItem = IsItemInLootTier(tmpItem);
|
||||
|
||||
if ((skipItem && !roundRobinTrashLoot) || (!skipItem && roundRobinTrashLoot))
|
||||
continue;
|
||||
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(GetLootGroupID());
|
||||
if (group) {
|
||||
group->MGroupMembers.writelock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
|
||||
int8 index = group->GetLastLooterIndex();
|
||||
if (index >= members->size()) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
GroupMemberInfo* gmi = members->at(index);
|
||||
if (gmi) {
|
||||
looter = gmi->member;
|
||||
}
|
||||
bool loopAttempted = false;
|
||||
while (looter) {
|
||||
if (!looter->IsPlayer()) {
|
||||
index++;
|
||||
if (index >= members->size()) {
|
||||
if (loopAttempted) {
|
||||
looter = nullptr;
|
||||
break;
|
||||
}
|
||||
loopAttempted = true;
|
||||
index = 0;
|
||||
}
|
||||
gmi = members->at(index);
|
||||
if (gmi) {
|
||||
looter = gmi->member;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
group->SetNextLooterIndex(index);
|
||||
group->MGroupMembers.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
|
||||
if (looter) {
|
||||
if (looter->IsPlayer()) {
|
||||
Item* item = LootItem(item_id);
|
||||
bool success = false;
|
||||
success = ((Player*)looter)->GetClient()->HandleLootItem(this, item, ((Player*)looter), roundRobinTrashLoot);
|
||||
|
||||
if (!success)
|
||||
AddLootItem(item);
|
||||
}
|
||||
else {
|
||||
Item* item = LootItem(item_id);
|
||||
safe_delete(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -264,6 +264,28 @@ struct TimedGridData {
|
|||
int32 widget_id;
|
||||
};
|
||||
|
||||
enum GroupLootMethod {
|
||||
METHOD_LEADER=0,
|
||||
METHOD_FFA=1,
|
||||
METHOD_LOTTO=2,
|
||||
METHOD_NEED_BEFORE_GREED=3,
|
||||
METHOD_ROUND_ROBIN=4
|
||||
};
|
||||
|
||||
enum AutoLootMode {
|
||||
METHOD_DISABLED=0,
|
||||
METHOD_ACCEPT=1,
|
||||
METHOD_DECLINE=2
|
||||
};
|
||||
|
||||
enum LootTier {
|
||||
ITEMS_ALL=0,
|
||||
ITEMS_TREASURED_PLUS=1,
|
||||
ITEMS_LEGENDARY_PLUS=2,
|
||||
ITEMS_FABLED_PLUS=3
|
||||
};
|
||||
|
||||
|
||||
class Spawn {
|
||||
public:
|
||||
Spawn();
|
||||
|
@ -941,6 +963,15 @@ public:
|
|||
MLootItems.unlock();
|
||||
}
|
||||
|
||||
int32 GetLootCount() {
|
||||
int32 loot_item_count = 0;
|
||||
MLootItems.lock();
|
||||
loot_item_count = loot_items.size();
|
||||
MLootItems.unlock();
|
||||
|
||||
return loot_item_count;
|
||||
}
|
||||
|
||||
void ClearNonBodyLoot() {
|
||||
|
||||
MLootItems.lock();
|
||||
|
@ -964,10 +995,14 @@ public:
|
|||
UnlockLoot();
|
||||
return coins;
|
||||
}
|
||||
void SetLootCoins(int32 val) {
|
||||
LockLoot();
|
||||
void SetLootCoins(int32 val, bool lockloot = true) {
|
||||
if(lockloot)
|
||||
LockLoot();
|
||||
|
||||
loot_coins = val;
|
||||
UnlockLoot();
|
||||
|
||||
if(lockloot)
|
||||
UnlockLoot();
|
||||
}
|
||||
void AddLootCoins(int32 coins) {
|
||||
LockLoot();
|
||||
|
@ -1016,7 +1051,7 @@ public:
|
|||
bool IsInSpawnGroup(Spawn* spawn);
|
||||
Spawn* IsSpawnGroupMembersAlive(Spawn* ignore_spawn=nullptr, bool npc_only = true);
|
||||
void UpdateEncounterState(int8 new_state);
|
||||
void CheckEncounterState(Entity* victim);
|
||||
void CheckEncounterState(Entity* victim, bool test_auto_lock = false);
|
||||
void AddTargetToEncounter(Entity* entity);
|
||||
|
||||
void SendSpawnChanges(bool val){ send_spawn_changes = val; }
|
||||
|
@ -1301,6 +1336,47 @@ public:
|
|||
|
||||
void SendGroupUpdate();
|
||||
|
||||
void OverrideLootMethod(GroupLootMethod newMethod) { loot_method = newMethod; }
|
||||
void SetLootMethod(GroupLootMethod method, int8 item_rarity = 0, int32 group_id = 0);
|
||||
int32 GetLootGroupID() { return loot_group_id; }
|
||||
GroupLootMethod GetLootMethod() { return loot_method; }
|
||||
int8 GetLootRarity() { return loot_rarity; }
|
||||
int32 GetLootTimeRemaining() { return loot_timer.GetRemainingTime(); }
|
||||
bool IsLootTimerRunning() { return loot_timer.Enabled(); }
|
||||
bool CheckLootTimer() { return loot_timer.Check(); }
|
||||
void DisableLootTimer() { return loot_timer.Disable(); }
|
||||
int32 GetLooterSpawnID() { return looter_spawn_id; }
|
||||
void SetLooterSpawnID(int32 id) { looter_spawn_id = id; }
|
||||
bool AddNeedGreedItemRequest(int32 item_id, int32 spawn_id, bool need_item);
|
||||
bool AddLottoItemRequest(int32 item_id, int32 spawn_id);
|
||||
void AddSpawnLootWindowCompleted(int32 spawn_id, bool status_);
|
||||
bool SetSpawnLootWindowCompleted(int32 spawn_id);
|
||||
bool HasSpawnLootWindowCompleted(int32 spawn_id);
|
||||
bool HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id);
|
||||
bool HasSpawnLottoEntry(int32 item_id, int32 spawn_id);
|
||||
void GetSpawnLottoEntries(int32 item_id, std::map<int32, int32>* out_entries);
|
||||
void GetLootItemsList(std::vector<int32>* out_entries);
|
||||
void GetSpawnNeedGreedEntries(int32 item_id, bool need_item, std::map<int32, int32>* out_entries);
|
||||
|
||||
bool HasLootWindowCompleted();
|
||||
bool IsLootWindowComplete() { return is_loot_complete; }
|
||||
void SetLootDispensed() { is_loot_dispensed = true; }
|
||||
bool IsLootDispensed() { return is_loot_dispensed; }
|
||||
std::map<int32, bool>* GetLootWindowList() { return &loot_complete; }
|
||||
void StartLootTimer(Spawn* looter);
|
||||
void CloseLoot(Spawn* sender);
|
||||
|
||||
void SetLootName(char* name) {
|
||||
if(name != nullptr) {
|
||||
loot_name = std::string(name);
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetLootName() { return loot_name.c_str(); }
|
||||
|
||||
bool IsItemInLootTier(Item* item);
|
||||
void DistributeGroupLoot_RoundRobin(std::vector<int32>* item_list, bool roundRobinTrashLoot = false); // trash loot is what falls under the item tier requirement by group options
|
||||
|
||||
mutable std::shared_mutex MIgnoredWidgets;
|
||||
std::map<int32, bool> ignored_widgets;
|
||||
|
||||
|
@ -1346,9 +1422,23 @@ protected:
|
|||
|
||||
void CheckProximities();
|
||||
Timer pause_timer;
|
||||
private:
|
||||
|
||||
private:
|
||||
int32 loot_group_id;
|
||||
GroupLootMethod loot_method;
|
||||
int8 loot_rarity;
|
||||
Timer loot_timer;
|
||||
int32 looter_spawn_id;
|
||||
vector<Item*> loot_items;
|
||||
int32 loot_coins;
|
||||
std::multimap<int32, int32> lotto_items;
|
||||
std::multimap<int32, std::pair<int32, bool>> need_greed_items;
|
||||
|
||||
std::map<int32, bool> loot_complete;
|
||||
bool is_loot_complete;
|
||||
bool is_loot_dispensed;
|
||||
std::string loot_name;
|
||||
|
||||
bool trap_triggered;
|
||||
int32 trap_state;
|
||||
int32 chest_drop_time;
|
||||
|
|
|
@ -2066,7 +2066,7 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
|
|||
if (!stricmp(prop_name, CHAR_PROPERTY_SPEED))
|
||||
{
|
||||
float new_speed = atof(prop_value);
|
||||
client->GetPlayer()->SetSpeed(new_speed,true);
|
||||
client->GetPlayer()->SetSpeed(new_speed, true);
|
||||
client->GetPlayer()->SetCharSheetChanged(true);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE))
|
||||
|
@ -2093,7 +2093,7 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
|
|||
else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
|
||||
|
||||
client->SetRegionDebug(val == 1);
|
||||
if (val)
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!");
|
||||
|
@ -2110,6 +2110,46 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
|
|||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages.");
|
||||
}
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTMETHOD))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_loot_method(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTITEMRARITY))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOSPLIT))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_auto_split(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPDEFAULTYELL))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_default_yell(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOLOCK))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_autolock(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPSOLOAUTOLOCK))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_solo_autolock(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_AUTOLOOTMETHOD))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(val);
|
||||
}
|
||||
else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOCKMETHOD))
|
||||
{
|
||||
int8 val = atoul(prop_value);
|
||||
client->GetPlayer()->GetInfoStruct()->set_group_lock_method(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -113,6 +113,15 @@ using namespace std;
|
|||
#define CHAR_PROPERTY_GMVISION "modify_gmvision"
|
||||
#define CHAR_PROPERTY_LUADEBUG "modify_luadebug"
|
||||
|
||||
#define CHAR_PROPERTY_GROUPLOOTMETHOD "group_loot_method"
|
||||
#define CHAR_PROPERTY_GROUPLOOTITEMRARITY "group_loot_item_rarity"
|
||||
#define CHAR_PROPERTY_GROUPAUTOSPLIT "group_auto_split"
|
||||
#define CHAR_PROPERTY_GROUPDEFAULTYELL "group_default_yell"
|
||||
#define CHAR_PROPERTY_GROUPAUTOLOCK "group_autolock"
|
||||
#define CHAR_PROPERTY_GROUPLOCKMETHOD "group_lock_method"
|
||||
#define CHAR_PROPERTY_GROUPSOLOAUTOLOCK "group_solo_autolock"
|
||||
#define CHAR_PROPERTY_AUTOLOOTMETHOD "group_auto_loot_method"
|
||||
|
||||
#define DB_TYPE_SPELLEFFECTS 1
|
||||
#define DB_TYPE_MAINTAINEDEFFECTS 2
|
||||
|
||||
|
|
|
@ -1038,10 +1038,16 @@ void Client::SendDefaultGroupOptions() {
|
|||
*/
|
||||
PacketStruct* default_options = configReader.getStruct("WS_DefaultGroupOptions", GetVersion());
|
||||
if (default_options) {
|
||||
default_options->setDataByName("loot_method", 1);
|
||||
default_options->setDataByName("loot_items_rarity", 1);
|
||||
default_options->setDataByName("auto_split_coin", 1);
|
||||
default_options->setDataByName("default_yell_method", 1);
|
||||
default_options->setDataByName("loot_method", GetPlayer()->GetInfoStruct()->get_group_loot_method());
|
||||
default_options->setDataByName("loot_items_rarity", GetPlayer()->GetInfoStruct()->get_group_loot_items_rarity());
|
||||
default_options->setDataByName("auto_split_coin", GetPlayer()->GetInfoStruct()->get_group_auto_split());
|
||||
default_options->setDataByName("default_yell_method", GetPlayer()->GetInfoStruct()->get_group_default_yell());
|
||||
default_options->setDataByName("group_autolock", GetPlayer()->GetInfoStruct()->get_group_autolock());
|
||||
default_options->setDataByName("default_group_lock_method", GetPlayer()->GetInfoStruct()->get_group_lock_method());
|
||||
if(GetVersion() > 561) {
|
||||
default_options->setDataByName("solo_autolock", GetPlayer()->GetInfoStruct()->get_group_solo_autolock());
|
||||
default_options->setDataByName("auto_loot_method", GetPlayer()->GetInfoStruct()->get_group_auto_loot_method());
|
||||
}
|
||||
EQ2Packet* app7 = default_options->serialize();
|
||||
QueuePacket(app7);
|
||||
safe_delete(default_options);
|
||||
|
@ -1131,6 +1137,66 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
|||
safe_delete(request);
|
||||
break;
|
||||
}
|
||||
case OP_DefaultGroupOptionsMsg: {
|
||||
PacketStruct* packet = configReader.getStruct("WS_DefaultGroupOptions", GetVersion());
|
||||
if (packet) {
|
||||
if (packet->LoadPacketData(app->pBuffer, app->size)) {
|
||||
packet->PrintPacket();
|
||||
int8 loot_method = packet->getType_int8_ByName("loot_method");
|
||||
int8 loot_items_rarity = packet->getType_int8_ByName("loot_items_rarity");
|
||||
int8 auto_split_coin = packet->getType_int8_ByName("auto_split_coin");
|
||||
int8 default_yell_method = packet->getType_int8_ByName("default_yell_method");
|
||||
int8 autolock = packet->getType_int8_ByName("group_autolock");
|
||||
int8 group_lock_method = packet->getType_int8_ByName("default_group_lock_method");
|
||||
int8 solo_autolock = packet->getType_int8_ByName("solo_autolock");
|
||||
int8 auto_loot_method = 0;
|
||||
|
||||
if (GetVersion() > 561) {
|
||||
auto_loot_method = packet->getType_int8_ByName("auto_loot_method");
|
||||
if (auto_loot_method > AutoLootMode::METHOD_DECLINE)
|
||||
auto_loot_method = AutoLootMode::METHOD_DECLINE;
|
||||
}
|
||||
GetPlayer()->GetInfoStruct()->set_group_loot_method(loot_method);
|
||||
GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(loot_items_rarity);
|
||||
GetPlayer()->GetInfoStruct()->set_group_auto_split(auto_split_coin);
|
||||
GetPlayer()->GetInfoStruct()->set_group_default_yell(default_yell_method);
|
||||
GetPlayer()->GetInfoStruct()->set_group_autolock(autolock);
|
||||
GetPlayer()->GetInfoStruct()->set_group_lock_method(group_lock_method);
|
||||
GetPlayer()->GetInfoStruct()->set_group_solo_autolock(solo_autolock);
|
||||
GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(auto_loot_method);
|
||||
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOOTMETHOD, (char*)std::to_string(loot_method).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOOTITEMRARITY, (char*)std::to_string(loot_items_rarity).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPAUTOSPLIT, (char*)std::to_string(auto_split_coin).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPDEFAULTYELL, (char*)std::to_string(default_yell_method).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPAUTOLOCK, (char*)std::to_string(autolock).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOCKMETHOD, (char*)std::to_string(group_lock_method).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPSOLOAUTOLOCK, (char*)std::to_string(solo_autolock).c_str());
|
||||
database.insertCharacterProperty(this, CHAR_PROPERTY_AUTOLOOTMETHOD, (char*)std::to_string(auto_loot_method).c_str());
|
||||
|
||||
if (this->GetPlayer()->GetGroupMemberInfo() && this->GetPlayer()->GetGroupMemberInfo()->leader)
|
||||
{
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(this->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
if (group)
|
||||
{
|
||||
GroupOptions goptions;
|
||||
goptions.loot_method = loot_method;
|
||||
goptions.loot_items_rarity = loot_items_rarity;
|
||||
goptions.auto_split = auto_split_coin;
|
||||
goptions.default_yell = default_yell_method;
|
||||
goptions.group_autolock = autolock;
|
||||
goptions.solo_autolock = solo_autolock;
|
||||
goptions.auto_loot_method = auto_loot_method;
|
||||
group->SetDefaultGroupOptions(&goptions);
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
safe_delete(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_MapRequest: {
|
||||
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_MapRequest", opcode, opcode);
|
||||
PacketStruct* packet = configReader.getStruct("WS_MapRequest", GetVersion());
|
||||
|
@ -1507,7 +1573,21 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
|||
}
|
||||
case OP_LootItemsRequestMsg: {
|
||||
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_LootItemsRequestMsg", opcode, opcode);
|
||||
HandleLoot(app);
|
||||
HandleLootItemRequestPacket(app);
|
||||
break;
|
||||
}
|
||||
case OP_StoppedLootingMsg: {
|
||||
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StoppedLootingMsg", opcode, opcode);
|
||||
if (app->size < sizeof(int32))
|
||||
break;
|
||||
|
||||
int32 loot_id = 0;
|
||||
memcpy(&loot_id, app->pBuffer, sizeof(int32));
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id);
|
||||
if(spawn) {
|
||||
spawn->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
|
||||
spawn->SetLooterSpawnID(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_WaypointSelectMsg: {
|
||||
|
@ -1561,8 +1641,9 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
|||
}
|
||||
else
|
||||
{
|
||||
if(zoning_destination)
|
||||
if(zoning_destination) {
|
||||
SetCurrentZone(zoning_destination);
|
||||
}
|
||||
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ReadyToZoneMsg", opcode, opcode);
|
||||
bool succeed_override_zone = true;
|
||||
if(!GetCurrentZone()) {
|
||||
|
@ -2664,28 +2745,64 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool Client::HandleLootItem(Spawn* entity, Item* item) {
|
||||
bool Client::HandleLootItem(Spawn* entity, Item* item, Spawn* target, bool overrideLootRestrictions) {
|
||||
if (!item) {
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find item to loot!");
|
||||
return false;
|
||||
}
|
||||
int32 conflictItemList = 0, conflictequipmentList = 0, conflictAppearanceEquipmentList = 0;
|
||||
int16 lore_stack_count = 0;
|
||||
if(((conflictItemList = player->item_list.CheckSlotConflict(item, true, true, &lore_stack_count)) == LORE ||
|
||||
(conflictequipmentList = player->equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE ||
|
||||
(conflictAppearanceEquipmentList = player->appearance_equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE) && !item->CheckFlag(STACK_LORE)) {
|
||||
|
||||
Player* lootingPlayer = player;
|
||||
Client* lootingClient = this;
|
||||
if (target != nullptr && target != lootingPlayer && target->IsPlayer()) {
|
||||
lootingPlayer = (Player*)target;
|
||||
lootingClient = lootingPlayer->GetClient();
|
||||
}
|
||||
|
||||
// needs to only be checked before expiration of loot restrictions
|
||||
if (!overrideLootRestrictions) {
|
||||
if (entity->GetLootGroupID() > 0 && (!lootingPlayer->GetGroupMemberInfo() || lootingPlayer->GetGroupMemberInfo()->group_id != entity->GetLootGroupID())) {
|
||||
LogWrite(LOOT__ERROR, 0, "Loot", "%s: Loot Group ID from %s did not match Item: %s (%u), expected group id %u.", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id, entity->GetLootGroupID());
|
||||
return false;
|
||||
}
|
||||
if (entity->GetLootMethod() != GroupLootMethod::METHOD_FFA) {
|
||||
switch (entity->GetLootMethod()) {
|
||||
case GroupLootMethod::METHOD_LEADER: {
|
||||
if (entity->GetLootGroupID() > 0 && (!lootingPlayer->GetGroupMemberInfo() || (lootingPlayer->GetGroupMemberInfo() && (lootingPlayer->GetGroupMemberInfo()->group_id != entity->GetLootGroupID() || !lootingPlayer->GetGroupMemberInfo()->leader)))) {
|
||||
LogWrite(LOOT__ERROR, 0, "Loot", "%s: Loot Attempt from %s was not allowed with Item: %s (%u), must be group leader.", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_LOTTO:
|
||||
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
||||
if (entity->IsLootTimerRunning()) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: Loot Timer is still running, flag player %s to lotto Item: %s (%u).", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (((conflictItemList = lootingPlayer->item_list.CheckSlotConflict(item, true, true, &lore_stack_count)) == LORE ||
|
||||
(conflictequipmentList = lootingPlayer->equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE ||
|
||||
(conflictAppearanceEquipmentList = lootingPlayer->appearance_equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE) && !item->CheckFlag(STACK_LORE)) {
|
||||
Message(CHANNEL_COLOR_RED, "You cannot loot %s due to lore conflict.", item->name.c_str());
|
||||
return false;
|
||||
}
|
||||
else if(conflictItemList == STACK_LORE || conflictequipmentList == STACK_LORE || conflictAppearanceEquipmentList == STACK_LORE) {
|
||||
else if (conflictItemList == STACK_LORE || conflictequipmentList == STACK_LORE || conflictAppearanceEquipmentList == STACK_LORE) {
|
||||
Message(CHANNEL_COLOR_RED, "You cannot loot %s due to stack lore conflict.", item->name.c_str());
|
||||
return false;
|
||||
}
|
||||
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 (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item)) {
|
||||
if (lootingPlayer->item_list.AssignItemToFreeSlot(item)) {
|
||||
|
||||
if (item->CheckFlag2(HEIRLOOM)) { // TODO: RAID Support
|
||||
GroupMemberInfo* gmi = lootingClient->GetPlayer()->GetGroupMemberInfo();
|
||||
if (gmi && gmi->group_id)
|
||||
{
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
|
@ -2693,18 +2810,18 @@ bool Client::HandleLootItem(Spawn* entity, Item* item) {
|
|||
{
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
if(members) {
|
||||
if (members) {
|
||||
for (int8 i = 0; i < members->size(); i++) {
|
||||
Entity* member = members->at(i)->member;
|
||||
if(!member)
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if ((member->GetZone() != this->GetPlayer()->GetZone()))
|
||||
if ((member->GetZone() != lootingClient->GetPlayer()->GetZone()))
|
||||
continue;
|
||||
|
||||
if(member->IsPlayer()) {
|
||||
item->grouped_char_ids.insert(std::make_pair(((Player*)member)->GetCharacterID(), true));
|
||||
item->save_needed = true;
|
||||
|
||||
if (member->IsPlayer()) {
|
||||
item->grouped_char_ids.insert(std::make_pair(((Player*)member)->GetCharacterID(), true));
|
||||
item->save_needed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2712,15 +2829,15 @@ bool Client::HandleLootItem(Spawn* entity, Item* item) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int8 type = CHANNEL_LOOT;
|
||||
if (entity) {
|
||||
Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
|
||||
lootingClient->Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
|
||||
}
|
||||
else {
|
||||
Message(type, "You found a %s.", item->CreateItemLink(GetVersion()).c_str());
|
||||
lootingClient->Message(type, "You found a %s.", item->CreateItemLink(GetVersion()).c_str());
|
||||
}
|
||||
Guild* guild = player->GetGuild();
|
||||
Guild* guild = lootingPlayer->GetGuild();
|
||||
if (guild && item->details.tier >= ITEM_TAG_LEGENDARY) {
|
||||
char adjective[32];
|
||||
int8 type;
|
||||
|
@ -2737,153 +2854,210 @@ bool Client::HandleLootItem(Spawn* entity, Item* item) {
|
|||
strncpy(adjective, "Legendary", sizeof(adjective) - 1);
|
||||
type = GUILD_EVENT_LOOTS_LEGENDARY_ITEM;
|
||||
}
|
||||
guild->AddNewGuildEvent(type, "%s has looted the %s %s", Timer::GetUnixTimeStamp(), true, player->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
|
||||
guild->SendMessageToGuild(type, "%s has looted the %s %s", player->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
|
||||
guild->AddNewGuildEvent(type, "%s has looted the %s %s", Timer::GetUnixTimeStamp(), true, lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
|
||||
guild->SendMessageToGuild(type, "%s has looted the %s %s", lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
|
||||
}
|
||||
|
||||
if (item->GetItemScript() && lua_interface)
|
||||
lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, player);
|
||||
|
||||
CheckPlayerQuestsItemUpdate(item);
|
||||
|
||||
if(GetVersion() <= 546) {
|
||||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, lootingPlayer);
|
||||
|
||||
lootingClient->CheckPlayerQuestsItemUpdate(item);
|
||||
|
||||
if (GetVersion() <= 546) {
|
||||
EQ2Packet* outapp = lootingPlayer->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
lootingClient->QueuePacket(outapp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "Could not find free slot to place item.");
|
||||
lootingClient->SimpleMessage(CHANNEL_COLOR_RED, "Could not find free slot to place item.");
|
||||
}
|
||||
else
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to loot item: Inventory is FULL.");
|
||||
lootingClient->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to loot item: Inventory is FULL.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Client::HandleLootItem(Spawn* entity, int32 item_id) {
|
||||
bool Client::HandleLootItemByID(Spawn* entity, int32 item_id, Spawn* target) {
|
||||
if (!entity) {
|
||||
return false;
|
||||
}
|
||||
Item* item = entity->LootItem(item_id);
|
||||
bool success = false;
|
||||
success = HandleLootItem(entity, item);
|
||||
if(!success)
|
||||
success = HandleLootItem(entity, item, target);
|
||||
if (!success)
|
||||
entity->AddLootItem(item);
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Client::HandleLoot(EQApplicationPacket* app) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_LootType", GetVersion());
|
||||
void Client::HandleLootItemRequestPacket(EQApplicationPacket* app) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_LootItem", GetVersion());
|
||||
if (packet) {
|
||||
if(packet->LoadPacketData(app->pBuffer, app->size)) {
|
||||
if (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;
|
||||
int32 target_id = packet->getType_int32_ByName("target_id");
|
||||
int8 button_clicked = packet->getType_int8_ByName("button_clicked");
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id);
|
||||
if (player->HasPendingLootItems(loot_id)) {
|
||||
Item* master_item = 0;
|
||||
if (loot_all) {
|
||||
vector<Item*>* items = player->GetPendingLootItems(loot_id);
|
||||
if (items) {
|
||||
for (int32 i = 0; loot_all && i < items->size(); 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);
|
||||
|
||||
if(GetVersion() <= 546) {
|
||||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
}
|
||||
}
|
||||
if(!spawn) {
|
||||
safe_delete(packet);
|
||||
return;
|
||||
}
|
||||
Item* item = nullptr;
|
||||
vector<Item*>* items = player->GetPendingLootItems(loot_id);
|
||||
if (items) {
|
||||
int32 item_id = packet->getType_int32_ByName("item_id_0");
|
||||
for (int32 i = 0; i < items->size(); i++) {
|
||||
Item* master_item = items->at(i);
|
||||
if (master_item && (loot_all || master_item->details.item_id == item_id)) {
|
||||
item = new Item(master_item);
|
||||
if (item) {
|
||||
loot_all = HandleLootItem(0, item);
|
||||
if (loot_all) {
|
||||
player->RemovePendingLootItem(loot_id, item->details.item_id);
|
||||
|
||||
if (GetVersion() <= 546) {
|
||||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
}
|
||||
}
|
||||
}
|
||||
safe_delete(items);
|
||||
|
||||
if (!loot_all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
safe_delete(items);
|
||||
safe_delete(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
spawn->LockLoot();
|
||||
bool unlockedLoot = false;
|
||||
if (spawn && !spawn->Alive() && spawn->IsNPC() && ((NPC*)spawn)->Brain()->CheckLootAllowed(player)) {
|
||||
if (loot_all) {
|
||||
switch (spawn->GetLootMethod()) {
|
||||
case GroupLootMethod::METHOD_LOTTO: {
|
||||
spawn->AddLottoItemRequest(0xFFFFFFFF, GetPlayer()->GetID());
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
||||
spawn->AddNeedGreedItemRequest(0xFFFFFFFF, GetPlayer()->GetID(), true);
|
||||
}
|
||||
default: {
|
||||
if (!unlockedLoot) {
|
||||
spawn->UnlockLoot();
|
||||
unlockedLoot = true;
|
||||
}
|
||||
int32 item_id = 0;
|
||||
while (loot_all && ((item_id = spawn->GetLootItemID()) > 0)) {
|
||||
loot_all = HandleLootItemByID(spawn, item_id, GetPlayer());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
spawn->UnlockLoot();
|
||||
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
|
||||
CloseLoot(loot_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
packet = configReader.getStruct("WS_LootItem", GetVersion());
|
||||
if (packet) {
|
||||
if(packet->LoadPacketData(app->pBuffer, app->size)) {
|
||||
item_id = packet->getType_int32_ByName("item_id");
|
||||
vector<Item*>* items = player->GetPendingLootItems(loot_id);
|
||||
if (items) {
|
||||
for (int32 i = 0; i < items->size(); 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);
|
||||
int8 item_count = packet->getType_int8_ByName("item_count");
|
||||
for (int8 cur = 0; cur < item_count; cur++) {
|
||||
char item_field_name[64];
|
||||
snprintf(item_field_name, 64, "item_id_%u", cur);
|
||||
int32 item_id = packet->getType_int32_ByName(item_field_name);
|
||||
Spawn* target = this->GetPlayer();
|
||||
if (target_id != 0xFFFFFFFF && GetPlayer()->GetGroupMemberInfo()) {
|
||||
Spawn* destTarget = GetPlayer()->GetSpawnWithPlayerID(target_id);
|
||||
if (destTarget && (!destTarget->IsPlayer() || !world.GetGroupManager()->IsInGroup(GetPlayer()->GetGroupMemberInfo()->group_id, ((Player*)destTarget)))) {
|
||||
SimpleMessage(CHANNEL_COMMAND_TEXT, "HACKS!!");
|
||||
safe_delete(packet);
|
||||
spawn->UnlockLoot();
|
||||
return;
|
||||
}
|
||||
target = destTarget;
|
||||
}
|
||||
safe_delete(packet);
|
||||
bool breakLoopAllLooted = false;
|
||||
switch (spawn->GetLootMethod()) {
|
||||
case GroupLootMethod::METHOD_LOTTO: {
|
||||
spawn->AddLottoItemRequest(item_id, GetPlayer()->GetID());
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
||||
if (button_clicked == 3) { // decline
|
||||
break;
|
||||
}
|
||||
if (GetVersion() <= 546) {
|
||||
button_clicked = 1; // selecting is need
|
||||
}
|
||||
spawn->AddNeedGreedItemRequest(item_id, GetPlayer()->GetID(), (button_clicked == 1));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!unlockedLoot) {
|
||||
spawn->UnlockLoot();
|
||||
unlockedLoot = true;
|
||||
}
|
||||
if (!loot_all) {
|
||||
HandleLootItemByID(spawn, item_id, target);
|
||||
}
|
||||
else {
|
||||
while (loot_all && ((item_id = spawn->GetLootItemID()) > 0)) {
|
||||
loot_all = HandleLootItemByID(spawn, item_id, target);
|
||||
}
|
||||
breakLoopAllLooted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (breakLoopAllLooted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!unlockedLoot) {
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO ||
|
||||
(spawn->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED && item_count >= spawn->GetLootCount())) {
|
||||
CloseLoot(loot_id);
|
||||
}
|
||||
}
|
||||
if(GetVersion() > 546) {
|
||||
|
||||
if (GetVersion() > 546) {
|
||||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
}
|
||||
Loot(0, player->GetPendingLootItems(loot_id), spawn);
|
||||
}
|
||||
else {
|
||||
if (spawn && !spawn->Alive() && spawn->IsNPC() && ((NPC*)spawn)->Brain()->CheckLootAllowed(player)) {
|
||||
if (loot_all) {
|
||||
while (loot_all && ((item_id = spawn->GetLootItemID()) > 0)) {
|
||||
loot_all = HandleLootItem(spawn, item_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
packet = configReader.getStruct("WS_LootItem", GetVersion());
|
||||
if (packet) {
|
||||
if(packet->LoadPacketData(app->pBuffer, app->size)) {
|
||||
item_id = packet->getType_int32_ByName("item_id");
|
||||
HandleLootItem(spawn, item_id);
|
||||
}
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
if(GetVersion() > 546) {
|
||||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
}
|
||||
Loot(spawn);
|
||||
if (!spawn->HasLoot()) {
|
||||
CloseLoot(loot_id);
|
||||
if (spawn->IsNPC())
|
||||
GetCurrentZone()->RemoveDeadSpawn(spawn);
|
||||
}
|
||||
if (spawn->GetLootMethod() != GroupLootMethod::METHOD_LOTTO && spawn->GetLootMethod() != GroupLootMethod::METHOD_NEED_BEFORE_GREED) {
|
||||
LootSpawnRequest(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.");
|
||||
spawn->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
|
||||
}
|
||||
|
||||
if (!spawn->HasLoot()) {
|
||||
CloseLoot(loot_id);
|
||||
if (spawn->IsNPC())
|
||||
GetCurrentZone()->RemoveDeadSpawn(spawn);
|
||||
}
|
||||
}
|
||||
else {
|
||||
spawn->UnlockLoot();
|
||||
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.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::HandleSkillInfoRequest(EQApplicationPacket* app) {
|
||||
|
@ -4499,7 +4673,9 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords, bool is_spell) {
|
|||
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Removing player from current zone...", __FUNCTION__);
|
||||
GetCurrentZone()->RemoveSpawn(player, false, true, true, true, !is_spell);
|
||||
|
||||
|
||||
GetPlayer()->DeleteSpellEffects(true);
|
||||
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Setting zone to '%s'...", __FUNCTION__, new_zone->GetZoneName());
|
||||
SetZoningDestination(new_zone);
|
||||
SetCurrentZone(new_zone);
|
||||
|
@ -5200,11 +5376,6 @@ void Client::ChangeTSLevel(int16 old_level, int16 new_level) {
|
|||
QueuePacket(master_trait_list.GetTraitListPacket(this));
|
||||
}
|
||||
|
||||
void Client::SendPendingLoot(int32 total_coins, Spawn* entity) {
|
||||
if (entity)
|
||||
Loot(total_coins, player->GetPendingLootItems(entity->GetID()), entity);
|
||||
}
|
||||
|
||||
void Client::CloseLoot(int32 spawn_id) {
|
||||
if (GetVersion() > 546) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_CloseWindow", GetVersion());
|
||||
|
@ -5218,7 +5389,8 @@ void Client::CloseLoot(int32 spawn_id) {
|
|||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
else if(spawn_id > 0){
|
||||
|
||||
if(spawn_id > 0){
|
||||
PacketStruct* packet = configReader.getStruct("WS_StoppedLooting", GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", spawn_id);
|
||||
|
@ -5227,6 +5399,11 @@ void Client::CloseLoot(int32 spawn_id) {
|
|||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
|
||||
Spawn* spawn = GetPlayer()->GetSpawnWithPlayerID(spawn_id);
|
||||
if(spawn) {
|
||||
spawn->CloseLoot(GetPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5266,7 +5443,7 @@ string Client::GetCoinMessage(int32 total_coins) {
|
|||
return message;
|
||||
}
|
||||
|
||||
void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
||||
void Client::SendLootResponsePacket(int32 total_coins, vector<Item*>* items, Spawn* entity, bool ignore_loot_tier) {
|
||||
if (!entity) {
|
||||
CloseLoot(0);
|
||||
return;
|
||||
|
@ -5277,7 +5454,7 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
string message = "";
|
||||
if (entity->GetHP() == 0) {
|
||||
message = "You loot ";
|
||||
entity->SetLootCoins(0);
|
||||
entity->SetLootCoins(0, false);
|
||||
}
|
||||
else
|
||||
message = "You receive ";
|
||||
|
@ -5290,30 +5467,48 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
}
|
||||
if (!items || items->size() == 0)
|
||||
CloseLoot(entity->GetID());
|
||||
|
||||
entity->StartLootTimer(GetPlayer());
|
||||
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateLoot", GetVersion());
|
||||
if (packet) {
|
||||
entity->AddSpawnLootWindowCompleted(GetPlayer()->GetID(), false);
|
||||
vector<Item*>::iterator itr;
|
||||
int32 packet_size = 0;
|
||||
EQ2Packet* outapp = 0;
|
||||
uchar* data = 0;
|
||||
vector<Item*> send_items;
|
||||
if (items && items->size() > 0) {
|
||||
for (int i = 0; i < items->size(); i++) {
|
||||
Item* item = (*items)[i];
|
||||
|
||||
if (entity->GetLootMethod() > GroupLootMethod::METHOD_FFA && !ignore_loot_tier) {
|
||||
bool skipItem = entity->IsItemInLootTier(item);
|
||||
if (!skipItem) {
|
||||
send_items.push_back(item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
send_items.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GetVersion() >= 284) {
|
||||
if (GetVersion() > 546) {
|
||||
if (items && items->size() > 0) {
|
||||
packet->setDataByName("loot_count", items->size());
|
||||
if (GetVersion() > 561) {
|
||||
if (send_items.size() > 0) {
|
||||
packet->setDataByName("loot_count", send_items.size());
|
||||
packet->setDataByName("display", 1);
|
||||
}
|
||||
packet->setDataByName("loot_type", 1);
|
||||
if (version >= 1096)
|
||||
packet->setDataByName("lotto_timeout", 0x78);
|
||||
else
|
||||
packet->setDataByName("lotto_timeout", 0x3C);
|
||||
packet->setDataByName("loot_type", entity->GetLootMethod());
|
||||
|
||||
packet->setDataByName("lotto_timeout", entity->GetLootTimeRemaining() / 1000);
|
||||
|
||||
packet->setDataByName("loot_id", entity->GetID());
|
||||
EQ2Packet* tmpPacket = packet->serialize();
|
||||
packet_size += tmpPacket->size;
|
||||
if (items && items->size() > 0) {
|
||||
data = new uchar[items->size() * 1000 + packet_size];
|
||||
memset(data, 0, items->size() * 1000 + packet_size);
|
||||
if (send_items.size() > 0) {
|
||||
data = new uchar[send_items.size() * 1000 + packet_size];
|
||||
memset(data, 0, send_items.size() * 1000 + packet_size);
|
||||
}
|
||||
else {
|
||||
data = new uchar[packet_size];
|
||||
|
@ -5324,8 +5519,8 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
ptr += tmpPacket->size;
|
||||
safe_delete(tmpPacket);
|
||||
Item* item = 0;
|
||||
if (items && items->size() > 0) {
|
||||
for (itr = items->begin(); itr != items->end(); itr++) {
|
||||
if (send_items.size() > 0) {
|
||||
for (itr = send_items.begin(); itr != send_items.end(); itr++) {
|
||||
item = *itr;
|
||||
memcpy(ptr, &item->details.item_id, sizeof(int32));
|
||||
ptr += sizeof(int32);
|
||||
|
@ -5340,7 +5535,7 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
else if (GetVersion() >= 860) {
|
||||
offset = 11;
|
||||
}
|
||||
else if (GetVersion() <= 546) {
|
||||
else if (GetVersion() <= 561) {
|
||||
offset = 19;
|
||||
}
|
||||
else {
|
||||
|
@ -5360,12 +5555,12 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
outapp = new EQ2Packet(OP_ClientCmdMsg, data, packet_size);
|
||||
}
|
||||
else {
|
||||
if (items && items->size() > 0) {
|
||||
packet->setArrayLengthByName("loot_count", items->size());
|
||||
if (send_items.size() > 0) {
|
||||
packet->setArrayLengthByName("loot_count", send_items.size());
|
||||
Item* item = 0;
|
||||
if (items && items->size() > 0) {
|
||||
if (send_items.size() > 0) {
|
||||
int i = 0;
|
||||
for (itr = items->begin(); itr != items->end(); itr++) {
|
||||
for (itr = send_items.begin(); itr != send_items.end(); itr++) {
|
||||
item = *itr;
|
||||
packet->setArrayDataByName("loot_id", item->details.item_id, i);
|
||||
packet->setItemArrayDataByName("item", item, GetPlayer(), i, 0, 2, true);
|
||||
|
@ -5374,17 +5569,17 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
}
|
||||
packet->setDataByName("display", 1);
|
||||
}
|
||||
packet->setDataByName("loot_type", 1); // normal
|
||||
packet->setDataByName("lotto_timeout", 0x3c); // 60 seconds
|
||||
packet->setDataByName("loot_type", entity->GetLootMethod()); // normal
|
||||
packet->setDataByName("lotto_timeout", entity->GetLootTimeRemaining() / 1000); // 60 seconds
|
||||
packet->setDataByName("spawn_id", entity->GetID());
|
||||
outapp = packet->serialize();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (items && items->size() > 0) {
|
||||
packet->setArrayLengthByName("loot_count", items->size());
|
||||
for (int i = 0; i < items->size(); i++) {
|
||||
Item* item = (*items)[i];
|
||||
if (send_items.size() > 0) {
|
||||
packet->setArrayLengthByName("loot_count", send_items.size());
|
||||
for (int i = 0; i < send_items.size(); i++) {
|
||||
Item* item = (send_items)[i];
|
||||
packet->setArrayDataByName("name", item->name.c_str(), i);
|
||||
packet->setArrayDataByName("item_id", item->details.item_id, i);
|
||||
packet->setArrayDataByName("count", item->details.count, i);
|
||||
|
@ -5412,18 +5607,162 @@ void Client::Loot(int32 total_coins, vector<Item*>* items, Spawn* entity) {
|
|||
|
||||
}
|
||||
|
||||
void Client::Loot(Spawn* entity, bool attemptDisarm) {
|
||||
if (entity->IsNPC() && ((NPC*)entity)->Brain()->CheckLootAllowed(GetPlayer())) {
|
||||
int32 total_coins = entity->GetLootCoins();
|
||||
bool Client::LootSpawnByMethod(Spawn* entity) {
|
||||
bool sentLoot = false;
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
GroupMemberInfo* gmi = GetPlayer()->GetGroupMemberInfo();
|
||||
if (gmi && gmi->group_id)
|
||||
{
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
if (group)
|
||||
{
|
||||
int8 auto_split_coin = group->GetGroupOptions()->auto_split;
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
int32 split_coin_per_player = 0;
|
||||
int32 coins_remain_after_split = entity->GetLootCoins();
|
||||
int32 total_coins = entity->GetLootCoins();
|
||||
|
||||
if (auto_split_coin) {
|
||||
int8 members_in_zone = 0;
|
||||
|
||||
for (int8 i = 0; i < members->size(); i++) {
|
||||
Entity* member = members->at(i)->member;
|
||||
if (!member || !member->IsPlayer())
|
||||
continue;
|
||||
|
||||
if (member->GetZone() != GetPlayer()->GetZone())
|
||||
continue;
|
||||
|
||||
members_in_zone++;
|
||||
}
|
||||
|
||||
if (members_in_zone < 1) // this should not happen, but divide by zero checked
|
||||
members_in_zone = 0;
|
||||
|
||||
split_coin_per_player = entity->GetLootCoins() / members_in_zone;
|
||||
coins_remain_after_split = entity->GetLootCoins() - (split_coin_per_player * members_in_zone);
|
||||
entity->SetLootCoins(0, false);
|
||||
}
|
||||
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: Group LootSpawnByMethod %u, auto coin split %u, split coin per player %u, remaining coin after split %u", entity->GetName(), entity->GetLootMethod(), auto_split_coin, split_coin_per_player, coins_remain_after_split);
|
||||
bool startWithLooter = true;
|
||||
|
||||
for (int8 i = 0; i < members->size(); i++) {
|
||||
Entity* member = members->at(i)->member;
|
||||
if (!member || !member->IsPlayer())
|
||||
continue;
|
||||
|
||||
if (member->GetZone() != GetPlayer()->GetZone())
|
||||
continue;
|
||||
|
||||
// this will make sure we properly send the loot window to the initial requester if there is no item rarity matches
|
||||
if (startWithLooter && member != GetPlayer())
|
||||
continue;
|
||||
else if (!startWithLooter && member == GetPlayer())
|
||||
continue;
|
||||
else if (startWithLooter) {
|
||||
i = 0;
|
||||
startWithLooter = false;
|
||||
}
|
||||
|
||||
if (auto_split_coin && (split_coin_per_player + coins_remain_after_split) > 0) {
|
||||
player->AddCoins(split_coin_per_player + coins_remain_after_split);
|
||||
if (((Player*)member)->GetClient()) {
|
||||
((Player*)member)->GetClient()->Message(CHANNEL_MONEY_SPLIT, "Your share of %s from the corpse of %s is %s.", GetCoinMessage(total_coins).c_str(), entity->GetLootName(), GetCoinMessage(split_coin_per_player + coins_remain_after_split).c_str());
|
||||
}
|
||||
if (coins_remain_after_split > 0) // overflow of coin division went to the first player
|
||||
coins_remain_after_split = 0;
|
||||
}
|
||||
switch (entity->GetLootMethod()) {
|
||||
case GroupLootMethod::METHOD_LOTTO:
|
||||
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
||||
if (((Player*)member)->GetClient()) {
|
||||
switch (member->GetInfoStruct()->get_group_auto_loot_method()) {
|
||||
case 1: { // lotto, need
|
||||
if (entity->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
|
||||
entity->AddLottoItemRequest(0xFFFFFFFF, GetPlayer()->GetID());
|
||||
}
|
||||
else { // *need* before greed
|
||||
entity->AddNeedGreedItemRequest(0xFFFFFFFF, GetPlayer()->GetID(), true);
|
||||
}
|
||||
entity->AddSpawnLootWindowCompleted(member->GetID(), true);
|
||||
// if it already exists we have to override the setting
|
||||
entity->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
|
||||
break;
|
||||
}
|
||||
case 2: { // decline
|
||||
entity->AddSpawnLootWindowCompleted(member->GetID(), true);
|
||||
// if it already exists we have to override the setting
|
||||
entity->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
|
||||
break;
|
||||
}
|
||||
default: { // presume 0 or higher than 2
|
||||
((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sentLoot = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_ROUND_ROBIN: {
|
||||
entity->AddSpawnLootWindowCompleted(member->GetID(), true);
|
||||
sentLoot = true;
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_LEADER: {
|
||||
if (member->GetGroupMemberInfo()->leader)
|
||||
((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity);
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_FFA: {
|
||||
if(member == GetPlayer()) {
|
||||
((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
return sentLoot;
|
||||
}
|
||||
void Client::LootSpawnRequest(Spawn* entity, bool attemptDisarm) {
|
||||
bool lootAllowed = false;
|
||||
bool sentLoot = false;
|
||||
std::vector<int32> item_list;
|
||||
if (entity->IsNPC()) {
|
||||
entity->LockLoot();
|
||||
Loot(total_coins, entity->GetLootItems(), entity);
|
||||
lootAllowed = ((NPC*)entity)->Brain()->CheckLootAllowed(GetPlayer());
|
||||
entity->UnlockLoot();
|
||||
|
||||
OpenChest(entity, attemptDisarm);
|
||||
}
|
||||
else
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time.");
|
||||
if (lootAllowed) {
|
||||
OpenChest(entity, attemptDisarm);
|
||||
}
|
||||
else {
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time.");
|
||||
return;
|
||||
}
|
||||
|
||||
entity->LockLoot();
|
||||
if (((NPC*)entity)->Brain()->CheckLootAllowed(GetPlayer())) {
|
||||
lootAllowed = true;
|
||||
if ((sentLoot = LootSpawnByMethod(entity))) {
|
||||
entity->GetLootItemsList(&item_list);
|
||||
}
|
||||
else {
|
||||
SendLootResponsePacket(entity->GetLootCoins(), entity->GetLootItems(), entity);
|
||||
}
|
||||
}
|
||||
entity->UnlockLoot();
|
||||
|
||||
if (lootAllowed) {
|
||||
entity->DistributeGroupLoot_RoundRobin(&item_list, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::OpenChest(Spawn* entity, bool attemptDisarm)
|
||||
|
@ -6572,7 +6911,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
}
|
||||
if (rewarded_coin > coin)
|
||||
coin = rewarded_coin;
|
||||
if (!quest && !was_displayed) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (!quest && !was_displayed) { //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (coin > 0) {
|
||||
player->AddCoins(coin);
|
||||
PlaySound("coin_cha_ching");
|
||||
|
@ -6582,7 +6921,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
packet2->setSubstructDataByName("reward_data", "reward", header);
|
||||
packet2->setSubstructDataByName("reward_data", "max_coin", coin);
|
||||
if (player->GetGuild() && !was_displayed) {
|
||||
if (!quest) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (!quest) { //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
player->GetInfoStruct()->add_status_points(status_points);
|
||||
player->SetCharSheetChanged(true);
|
||||
}
|
||||
|
@ -6606,7 +6945,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
packet2->setArrayDataByName("reward_id", item->details.item_id, i);
|
||||
packet2->setItemArrayDataByName("item", item, player, i, 0, -1);
|
||||
}
|
||||
if(!quest) //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if(!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it
|
||||
}
|
||||
}
|
||||
|
@ -6617,7 +6956,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
packet2->setArrayDataByName("reward_id", item->details.item_id, i);
|
||||
packet2->setItemArrayDataByName("item", item, player, i, 0, -1);
|
||||
}
|
||||
if(!quest) //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if(!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it
|
||||
|
||||
i++;
|
||||
|
@ -6630,7 +6969,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
if (item) {
|
||||
packet2->setArrayDataByName("select_reward_id", item->details.item_id, i);
|
||||
packet2->setItemArrayDataByName("select_item", item, player, i, 0, -1);
|
||||
if (!quest) //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
player->AddPendingSelectableItemReward(source_id, item); //item reference will be deleted after the player selects one
|
||||
}
|
||||
}
|
||||
|
@ -6712,7 +7051,7 @@ void Client::DisplayQuestComplete(Quest* quest, bool tempReward, std::string cus
|
|||
if (!quest)
|
||||
return;
|
||||
|
||||
if (GetVersion() <= 546) {
|
||||
if (GetVersion() <= 561) {
|
||||
DisplayQuestRewards(quest, 0, quest->GetRewardItems(), quest->GetSelectableRewardItems(), quest->GetRewardFactions(), "Quest Complete!", quest->GetStatusPoints(), tempReward ? customDescription.c_str() : quest->GetCompletedDescription(), was_displayed);
|
||||
return;
|
||||
}
|
||||
|
@ -6907,6 +7246,7 @@ void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<C
|
|||
packet->setDataByName("conversation_id", conversation_id);
|
||||
packet->setDataByName("text", text);
|
||||
packet->setDataByName("language", language); // default 0
|
||||
packet->setDataByName("enable_blue_ui", 0); // default 0
|
||||
packet->setDataByName("can_close", can_close); // default 1
|
||||
conversation_map[conversation_id].clear();
|
||||
if (conversations) {
|
||||
|
@ -8046,7 +8386,11 @@ void Client::SendSellMerchantList(bool sell) {
|
|||
vector<Item*> sellable_items;
|
||||
map<int32, Item*>::iterator test_itr;
|
||||
for (test_itr = items->begin(); test_itr != items->end(); test_itr++) {
|
||||
if (test_itr->second && !test_itr->second->CheckFlag(NO_VALUE))
|
||||
bool isbagwithitems = false;
|
||||
if (test_itr->second && test_itr->second->IsBag() && (test_itr->second->details.num_slots - test_itr->second->details.num_free_slots != test_itr->second->details.num_slots))
|
||||
isbagwithitems = true;
|
||||
|
||||
if (test_itr->second && !test_itr->second->CheckFlag(NO_VALUE) && (isbagwithitems == false) && (test_itr->second->details.inv_slot_id != -3) && (test_itr->second->details.inv_slot_id != -4))
|
||||
sellable_items.push_back(test_itr->second);
|
||||
}
|
||||
packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
|
||||
|
@ -9417,8 +9761,10 @@ void Client::SearchStore(int32 page) {
|
|||
}
|
||||
|
||||
void Client::SetReadyForUpdates() {
|
||||
if (!ready_for_updates)
|
||||
if (!ready_for_updates) {
|
||||
database.loadCharacterProperties(this);
|
||||
SendDefaultGroupOptions();
|
||||
}
|
||||
|
||||
ready_for_updates = true;
|
||||
|
||||
|
|
|
@ -181,9 +181,9 @@ public:
|
|||
void SendPlayerDeathWindow();
|
||||
float DistanceFrom(Client* client);
|
||||
void SendDefaultGroupOptions();
|
||||
bool HandleLootItem(Spawn* entity, int32 item_id);
|
||||
bool HandleLootItem(Spawn* entity, Item* item);
|
||||
void HandleLoot(EQApplicationPacket* app);
|
||||
bool HandleLootItemByID(Spawn* entity, int32 item_id, Spawn* target);
|
||||
bool HandleLootItem(Spawn* entity, Item* item, Spawn* target=nullptr, bool overrideLootRestrictions = false);
|
||||
void HandleLootItemRequestPacket(EQApplicationPacket* app);
|
||||
void HandleSkillInfoRequest(EQApplicationPacket* app);
|
||||
void HandleExamineInfoRequest(EQApplicationPacket* app);
|
||||
void HandleQuickbarUpdateRequest(EQApplicationPacket* app);
|
||||
|
@ -263,9 +263,9 @@ public:
|
|||
void Save();
|
||||
bool remove_from_list;
|
||||
void CloseLoot(int32 spawn_id);
|
||||
void SendPendingLoot(int32 total_coins, Spawn* entity);
|
||||
void Loot(int32 total_coins, vector<Item*>* items, Spawn* entity);
|
||||
void Loot(Spawn* entity, bool attemptDisarm=true);
|
||||
void SendLootResponsePacket(int32 total_coins, vector<Item*>* items, Spawn* entity, bool ignore_loot_tier = false);
|
||||
void LootSpawnRequest(Spawn* entity, bool attemptDisarm=true);
|
||||
bool LootSpawnByMethod(Spawn* entity);
|
||||
void OpenChest(Spawn* entity, bool attemptDisarm=true);
|
||||
void CastGroupOrSelf(Entity* source, uint32 spellID, uint32 spellTier=1, float restrictiveRadius=0.0f);
|
||||
void CheckPlayerQuestsKillUpdate(Spawn* spawn);
|
||||
|
|
|
@ -939,6 +939,8 @@ bool ZoneServer::AggroVictim(NPC* npc, Spawn* victim, Client* client)
|
|||
else
|
||||
npc->InCombat(true);
|
||||
}
|
||||
|
||||
victim->CheckEncounterState((Entity*)npc, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1270,10 +1272,201 @@ bool ZoneServer::CombatProcess(Spawn* spawn) {
|
|||
|
||||
if (spawn && spawn->IsEntity())
|
||||
((Entity*)spawn)->ProcessCombat();
|
||||
if (spawn && !spawn->Alive() && !spawn->IsLootDispensed()) {
|
||||
LootProcess(spawn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ZoneServer::LootProcess(Spawn* spawn) {
|
||||
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_ROUND_ROBIN) {
|
||||
spawn->LockLoot();
|
||||
if (spawn->CheckLootTimer() || spawn->IsLootWindowComplete()) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: Dispensing loot, loot window was completed? %s.", spawn->GetName(), spawn->IsLootWindowComplete() ? "YES" : "NO");
|
||||
spawn->DisableLootTimer();
|
||||
spawn->SetLootDispensed();
|
||||
Spawn* looter = nullptr;
|
||||
if (spawn->GetLootGroupID() < 1 && spawn->GetLootWindowList()->size() > 0) {
|
||||
std::map<int32, bool>::iterator itr;
|
||||
|
||||
for (itr = spawn->GetLootWindowList()->begin(); itr != spawn->GetLootWindowList()->end(); itr++) {
|
||||
Spawn* entry = GetSpawnByID(itr->first, true);
|
||||
if (entry->IsPlayer()) {
|
||||
looter = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32 item_id = 0;
|
||||
std::vector<int32> item_list;
|
||||
spawn->GetLootItemsList(&item_list);
|
||||
spawn->UnlockLoot();
|
||||
|
||||
std::vector<int32>::iterator item_itr;
|
||||
|
||||
for (item_itr = item_list.begin(); item_itr != item_list.end(); item_itr++) {
|
||||
int32 item_id = *item_itr;
|
||||
Item* tmpItem = master_item_list.GetItem(item_id);
|
||||
|
||||
bool skipItem = spawn->IsItemInLootTier(tmpItem);
|
||||
|
||||
if (skipItem)
|
||||
continue;
|
||||
|
||||
if (looter) {
|
||||
if (looter->IsPlayer()) {
|
||||
|
||||
Item* item = spawn->LootItem(item_id);
|
||||
bool success = false;
|
||||
success = ((Player*)looter)->GetClient()->HandleLootItem(spawn, item, ((Player*)looter));
|
||||
|
||||
if (!success)
|
||||
spawn->AddLootItem(item);
|
||||
}
|
||||
else {
|
||||
Item* item = spawn->LootItem(item_id);
|
||||
safe_delete(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spawn->GetLootGroupID() > 0) {
|
||||
int32 item_id = 0;
|
||||
std::vector<int32> item_list;
|
||||
spawn->GetLootItemsList(&item_list);
|
||||
spawn->UnlockLoot();
|
||||
spawn->DistributeGroupLoot_RoundRobin(&item_list);
|
||||
}
|
||||
|
||||
if (!spawn->HasLoot()) {
|
||||
if (spawn->IsNPC())
|
||||
RemoveDeadSpawn(spawn);
|
||||
}
|
||||
else {
|
||||
spawn->LockLoot();
|
||||
spawn->SetLootMethod(GroupLootMethod::METHOD_FFA, 0, 0);
|
||||
spawn->SetLooterSpawnID(0);
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
}
|
||||
else {
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
}
|
||||
else if ((spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO || spawn->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED) && spawn->IsLootTimerRunning()) {
|
||||
spawn->LockLoot();
|
||||
if (spawn->CheckLootTimer() || spawn->IsLootWindowComplete()) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: Dispensing loot, loot window was completed? %s.", spawn->GetName(), spawn->IsLootWindowComplete() ? "YES" : "NO");
|
||||
spawn->DisableLootTimer();
|
||||
spawn->SetLootDispensed();
|
||||
|
||||
// identify any clients that still have the loot window open, close it out
|
||||
CloseSpawnLootWindow(spawn);
|
||||
|
||||
// lotto items while we have loot items in the list
|
||||
int32 item_id = 0;
|
||||
std::vector<int32> item_list;
|
||||
spawn->GetLootItemsList(&item_list);
|
||||
spawn->UnlockLoot();
|
||||
|
||||
std::vector<int32>::iterator item_itr;
|
||||
|
||||
for (item_itr = item_list.begin(); item_itr != item_list.end(); item_itr++) {
|
||||
int32 item_id = *item_itr;
|
||||
Item* tmpItem = master_item_list.GetItem(item_id);
|
||||
|
||||
bool skipItem = spawn->IsItemInLootTier(tmpItem);
|
||||
|
||||
if (skipItem)
|
||||
continue;
|
||||
|
||||
std::map<int32, int32> out_entries;
|
||||
std::map<int32, int32>::iterator out_itr;
|
||||
bool itemNeed = true;
|
||||
switch (spawn->GetLootMethod()) {
|
||||
case GroupLootMethod::METHOD_LOTTO: {
|
||||
spawn->GetSpawnLottoEntries(item_id, &out_entries);
|
||||
break;
|
||||
}
|
||||
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
||||
spawn->GetSpawnNeedGreedEntries(item_id, true, &out_entries);
|
||||
if (out_entries.size() < 1) {
|
||||
spawn->GetSpawnNeedGreedEntries(item_id, false, &out_entries);
|
||||
itemNeed = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (out_entries.size() < 1) {
|
||||
LogWrite(LOOT__INFO, 0, "Loot", "%s: No spawns matched for loot attempt of %s (%u), skip item.", spawn->GetName(), tmpItem ? tmpItem->name.c_str() : "Unknown", item_id);
|
||||
continue;
|
||||
}
|
||||
Spawn* looter = nullptr;
|
||||
int32 curWinValue = 0;
|
||||
for (out_itr = out_entries.begin(); out_itr != out_entries.end(); out_itr++) {
|
||||
Spawn* entry = GetSpawnByID(out_itr->first, true);
|
||||
if ((out_itr->second > curWinValue) || looter == nullptr) {
|
||||
curWinValue = out_itr->second;
|
||||
looter = entry;
|
||||
}
|
||||
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
|
||||
world.GetGroupManager()->SendGroupMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %u on %s.", entry->GetName(), out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
}
|
||||
else {
|
||||
world.GetGroupManager()->SendGroupMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %s (%u) on %s.", entry->GetName(), itemNeed ? "NEED" : "GREED", out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
if (looter) {
|
||||
if (looter->IsPlayer()) {
|
||||
Item* item = spawn->LootItem(item_id);
|
||||
bool success = false;
|
||||
success = ((Player*)looter)->GetClient()->HandleLootItem(spawn, item, ((Player*)looter));
|
||||
|
||||
if (!success)
|
||||
spawn->AddLootItem(item);
|
||||
}
|
||||
else {
|
||||
Item* item = spawn->LootItem(item_id);
|
||||
safe_delete(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spawn->HasLoot()) {
|
||||
if (spawn->IsNPC())
|
||||
RemoveDeadSpawn(spawn);
|
||||
}
|
||||
else {
|
||||
spawn->LockLoot();
|
||||
spawn->SetLootMethod(GroupLootMethod::METHOD_FFA, 0, 0);
|
||||
spawn->SetLooterSpawnID(0);
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
}
|
||||
else {
|
||||
spawn->UnlockLoot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneServer::CloseSpawnLootWindow(Spawn* spawn) {
|
||||
if (spawn->GetLootWindowList()->size() > 0) {
|
||||
std::map<int32, bool>::iterator itr;
|
||||
for (itr = spawn->GetLootWindowList()->begin(); itr != spawn->GetLootWindowList()->end(); itr++) {
|
||||
if (itr->second)
|
||||
continue;
|
||||
|
||||
itr->second = true;
|
||||
Spawn* looter = GetSpawnByID(itr->first, true);
|
||||
if (looter && looter->IsPlayer() && ((Player*)looter)->GetClient()) {
|
||||
LogWrite(LOOT__DEBUG, 0, "Loot", "%s: Close loot for player %s.", spawn->GetName(), looter->GetName());
|
||||
((Player*)looter)->GetClient()->CloseLoot(spawn->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void ZoneServer::AddPendingDelete(Spawn* spawn) {
|
||||
MSpawnDeleteList.writelock(__FUNCTION__, __LINE__);
|
||||
spawn->SetDeletedSpawn(true);
|
||||
|
@ -2510,12 +2703,13 @@ void ZoneServer::ProcessSpawnLocations()
|
|||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
void ZoneServer::AddLoot(NPC* npc, Spawn* killer){
|
||||
void ZoneServer::AddLoot(NPC* npc, Spawn* killer, GroupLootMethod loot_method, int8 item_rarity, int32 group_id){
|
||||
// this function is ran twice, first on spawn of mob, then at death of mob (gray mob check and no_drop_quest_completed_id check)
|
||||
|
||||
// first we see if the skipping of gray mobs loot is enabled, then we move all non body drops
|
||||
if(killer)
|
||||
{
|
||||
npc->SetLootMethod(loot_method, item_rarity, group_id);
|
||||
int8 skip_loot_gray_mob_flag = rule_manager.GetGlobalRule(R_Loot, SkipLootGrayMob)->GetInt8();
|
||||
if(skip_loot_gray_mob_flag) {
|
||||
Player* player = 0;
|
||||
|
@ -3296,7 +3490,11 @@ void ZoneServer::RemoveClient(Client* client)
|
|||
loginserver.SendImmediateEquipmentUpdatesForChar(client->GetPlayer()->GetCharacterID());
|
||||
|
||||
if (!client->IsZoning())
|
||||
{
|
||||
{
|
||||
client->SaveSpells();
|
||||
|
||||
client->GetPlayer()->DeleteSpellEffects(true);
|
||||
|
||||
if ((guild = client->GetPlayer()->GetGuild()) != NULL)
|
||||
guild->GuildMemberLogoff(client->GetPlayer());
|
||||
|
||||
|
@ -3345,10 +3543,6 @@ void ZoneServer::RemoveClient(Client* client)
|
|||
if (spawn)
|
||||
((Bot*)spawn)->Camp();
|
||||
}
|
||||
|
||||
client->SaveSpells();
|
||||
|
||||
client->GetPlayer()->DeleteSpellEffects(true);
|
||||
|
||||
if(dismissPets) {
|
||||
((Entity*)client->GetPlayer())->DismissAllPets();
|
||||
|
@ -4699,7 +4893,19 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
|
|||
else {
|
||||
Entity* hated = ((NPC*)dead)->Brain()->GetMostHated();
|
||||
if(hated) {
|
||||
AddLoot((NPC*)dead, hated);
|
||||
GroupLootMethod loot_method = GroupLootMethod::METHOD_FFA;
|
||||
int8 item_rarity = 0;
|
||||
if(hated->GetGroupMemberInfo()) {
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(hated->GetGroupMemberInfo()->group_id);
|
||||
if (group) {
|
||||
loot_method = (GroupLootMethod)group->GetGroupOptions()->loot_method;
|
||||
item_rarity = group->GetGroupOptions()->loot_items_rarity;
|
||||
LogWrite(LOOT__DEBUG, 0, "Loot", "%s: Loot method set to %u.", dead->GetName(), loot_method);
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
AddLoot((NPC*)dead, hated, loot_method, item_rarity, hated->GetGroupMemberInfo() ? hated->GetGroupMemberInfo()->group_id : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ public:
|
|||
void ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, const char* value);
|
||||
void SetSpawnCommand(Spawn* spawn, int8 type, char* value, Client* client = 0);
|
||||
void SetSpawnCommand(int32 spawn_id, int8 type, char* value, Client* client = 0);
|
||||
void AddLoot(NPC* npc, Spawn* killer = nullptr);
|
||||
void AddLoot(NPC* npc, Spawn* killer = nullptr, GroupLootMethod loot_method = GroupLootMethod::METHOD_FFA, int8 item_rarity = 0, int32 group_id = 0);
|
||||
|
||||
NPC* AddNPCSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry);
|
||||
Object* AddObjectSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry);
|
||||
|
@ -795,6 +795,8 @@ private:
|
|||
/// <summary>Checks to see if it is time to remove a spawn and removes it</summary>
|
||||
/// <param name='force_delete_all'>Forces all spawns scheduled to be removed regardless of time</param>
|
||||
bool CombatProcess(Spawn* spawn); // never used outside zone server
|
||||
void LootProcess(Spawn* spawn);
|
||||
void CloseSpawnLootWindow(Spawn* spawn);
|
||||
void InitWeather(); // never used outside zone server
|
||||
///<summary>Dismiss all pets in the zone, useful when the spell process needs to be reloaded</summary>
|
||||
void DismissAllPets(); // never used outside zone server
|
||||
|
|
|
@ -222,7 +222,7 @@ void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool
|
|||
new_name = string(name).append("_").append(ds->GetStringName());
|
||||
else
|
||||
new_name = string(name).append("_").append(ds->GetStringName()).append("_").append(tmp);
|
||||
|
||||
|
||||
DataStruct* ds2 = new DataStruct(new_name.c_str(), ds->GetType(),ds->GetLength(), ds->GetType2());
|
||||
|
||||
if(!array_packet && strlen(ds->GetArraySizeVariable()) > 1)
|
||||
|
@ -231,9 +231,8 @@ void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool
|
|||
ds2->SetOversizedByte(ds->GetOversizedByte());
|
||||
ds2->SetDefaultValue(ds->GetDefaultValue());
|
||||
ds2->SetMaxArraySize(ds->GetMaxArraySize());
|
||||
ds2->SetIfSetVariable(ds->GetIfSetVariable());
|
||||
ds2->SetIfNotSetVariable(ds->GetIfNotSetVariable());
|
||||
ds2->SetIfEqualsVariable(ds->GetIfEqualsVariable());
|
||||
ds2->SetIfSetVariable(ds->GetIfSetVariable() ? ds->GetIfSetVariable() : if_variable);
|
||||
ds2->SetIfNotSetVariable(ds->GetIfSetVariable() ? ds->GetIfNotSetVariable() : if_not_variable);
|
||||
ds2->SetIfNotEqualsVariable(ds->GetIfNotEqualsVariable());
|
||||
ds2->SetIfFlagNotSetVariable(ds->GetIfFlagNotSetVariable());
|
||||
ds2->SetIfFlagSetVariable(ds->GetIfFlagSetVariable());
|
||||
|
|
|
@ -1130,6 +1130,7 @@ bool PacketStruct::LoadPacketData(uchar* data, int32 data_len, bool create_color
|
|||
data_struct = *itr;
|
||||
if (!data_struct->AddToStruct())
|
||||
continue;
|
||||
|
||||
if (data_struct->GetIfSet() && data_struct->GetIfSetVariable()) {
|
||||
string varname = string(data_struct->GetIfSetVariable());
|
||||
if (varname.find(",") < 0xFFFFFFFF) {
|
||||
|
|
|
@ -88,7 +88,7 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="body_size" Type="float" Size="1" />
|
||||
<Data ElementName="body_age" Type="float" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="CreateCharacter" ClientVersion="547" OpcodeName="OP_CreateCharacterRequestMsg">
|
||||
<Struct Name="CreateCharacter" ClientVersion="562" OpcodeName="OP_CreateCharacterRequestMsg">
|
||||
<Data ElementName="unknown0" Type="int8" />
|
||||
<Data ElementName="unknown1" Type="int32" />
|
||||
<Data ElementName="account_id" Type="int32" />
|
||||
|
|
|
@ -12022,14 +12022,6 @@
|
|||
<Data ElementName="duration" Type="float" Size="1" />
|
||||
<Data ElementName="footer" Substruct="Substruct_ItemFooter" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_MerchantItemGeneric" ClientVersion="63119" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqExamineInfoCmd">
|
||||
<Data ElementName="header" Substruct="Substruct_MerchantItemDescription" Size="1" />
|
||||
<Data ElementName="footer" Substruct="Substruct_ItemFooter" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_MerchantItemGeneric" ClientVersion="63119" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqExamineInfoCmd">
|
||||
<Data ElementName="header" Substruct="Substruct_MerchantItemDescription" Size="1" />
|
||||
<Data ElementName="footer" Substruct="Substruct_ItemFooter" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_MerchantItemDecoration" ClientVersion="63119" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqExamineInfoCmd">
|
||||
<Data ElementName="header" Substruct="Substruct_MerchantItemDescription" Size="1" />
|
||||
<Data ElementName="decoration_name" Type="EQ2_16Bit_String" Size="1" />
|
||||
|
|
|
@ -42,7 +42,7 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="passCode" Type="int32" Size="1" />
|
||||
<Data ElementName="version" Type="int16" />
|
||||
</Struct>
|
||||
<Struct Name="LS_LoginRequest" ClientVersion="547" OpcodeName="OP_LoginRequestMsg">
|
||||
<Struct Name="LS_LoginRequest" ClientVersion="562" OpcodeName="OP_LoginRequestMsg">
|
||||
<Data ElementName="accesscode" Type="EQ2_16BitString" />
|
||||
<Data ElementName="unknown1" Type="EQ2_16BitString" />
|
||||
<Data ElementName="username" Type="EQ2_16BitString" />
|
||||
|
@ -95,7 +95,7 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="allowed_races" Type="int32" Size="1" />
|
||||
</Data>
|
||||
</Struct>
|
||||
<Struct Name="LS_WorldList" ClientVersion="547" OpcodeName="OP_WorldListMsg">
|
||||
<Struct Name="LS_WorldList" ClientVersion="562" OpcodeName="OP_WorldListMsg">
|
||||
<Data ElementName="num_worlds" Type="int8" />
|
||||
<Data ElementName="world_list" Type="Array" ArraySizeVariable="num_worlds">
|
||||
<Data ElementName="id" Type="int32" Size="1" />
|
||||
|
@ -322,7 +322,7 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="soga_hair_face_color" Type="EQ2_Color" />
|
||||
<Data ElementName="soga_hair_face_highlight_color" Type="EQ2_Color" />
|
||||
</Struct>
|
||||
<Struct Name="CharSelectProfile" ClientVersion="547">
|
||||
<Struct Name="CharSelectProfile" ClientVersion="562">
|
||||
<Data ElementName="version" Type="int32" Size="1" />
|
||||
<Data ElementName="charid" Type="int32" Size="1" />
|
||||
<Data ElementName="server_id" Type="int32" Size="1" />
|
||||
|
|
|
@ -7245,34 +7245,34 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="lotto_timeout" Type="int32" />
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
</Struct>
|
||||
<Struct Name="WS_LootType" ClientVersion="1" OpcodeName="OP_LootItemsRequestMsg" >
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
<Data ElementName="loot_all" Type="int8" />
|
||||
<Data ElementName="unknown2" Type="int32" />
|
||||
</Struct>
|
||||
<Struct Name="WS_LootType" ClientVersion="882" OpcodeName="OP_LootItemsRequestMsg" >
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
<Data ElementName="unknown" Type="int8" />
|
||||
<Data ElementName="loot_all" Type="int8" />
|
||||
<Data ElementName="unknown2" Type="int32" />
|
||||
</Struct>
|
||||
|
||||
<Struct Name="WS_LootItem" ClientVersion="1" OpcodeName="OP_LootItemsRequestMsg" >
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
<Data ElementName="loot_all" Type="int8" />
|
||||
<Data ElementName="unknown2" Type="int8" />
|
||||
<Data ElementName="item_id" Type="int32" />
|
||||
<Data ElementName="unknown3" Type="int8" />
|
||||
<Data ElementName="unknown4" Type="int32" />
|
||||
<Data ElementName="item_count" Type="int8" IfVariableNotSet="loot_all"/>
|
||||
<Data ElementName="item_list" Type="Array" ArraySizeVariable="item_count" IfVariableNotSet="loot_all">
|
||||
<Data ElementName="item_id" Type="int32" IfVariableNotSet="loot_all"/>
|
||||
</Data>
|
||||
<Data ElementName="target_id" Type="int32" />
|
||||
</Struct>
|
||||
<Struct Name="WS_LootItem" ClientVersion="546" OpcodeName="OP_LootItemsRequestMsg" >
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
<Data ElementName="loot_all" Type="int8" />
|
||||
<Data ElementName="item_count" Type="int8" IfVariableNotSet="loot_all"/>
|
||||
<Data ElementName="item_list" Type="Array" ArraySizeVariable="item_count" IfVariableNotSet="loot_all">
|
||||
<Data ElementName="item_id" Type="int32" IfVariableNotSet="loot_all"/>
|
||||
</Data>
|
||||
<Data ElementName="target_id" Type="int32" />
|
||||
</Struct>
|
||||
<Struct Name="WS_LootItem" ClientVersion="882" OpcodeName="OP_LootItemsRequestMsg" >
|
||||
<Data ElementName="loot_id" Type="int32" />
|
||||
<Data ElementName="unknown" Type="int8" />
|
||||
<Data ElementName="loot_all" Type="int8" />
|
||||
<Data ElementName="unknown2" Type="int8" />
|
||||
<Data ElementName="item_id" Type="int32" />
|
||||
<Data ElementName="unknown3" Type="int8" />
|
||||
<Data ElementName="unknown4" Type="int32" />
|
||||
<Data ElementName="item_count" Type="int8" IfVariableNotSet="loot_all"/>
|
||||
<Data ElementName="item_list" Type="Array" ArraySizeVariable="item_count" IfVariableNotSet="loot_all">
|
||||
<Data ElementName="item_id" Type="int32" IfVariableNotSet="loot_all"/>
|
||||
</Data>
|
||||
<Data ElementName="button_clicked" Type="int8" />
|
||||
<Data ElementName="target_id" Type="int32" />
|
||||
</Struct>
|
||||
<Struct Name="WS_UpdateBank" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateBankCmd" >
|
||||
<Data ElementName="spawn_id" Type="int32" />
|
||||
|
@ -7815,8 +7815,8 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="onscreen_update_text" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="onscreen_update_text2" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="onscreen_update_icon" Type="int16" Size="1" />
|
||||
<Data ElementName="unknown6" Type="int8" Size="1" />
|
||||
<Data ElementName="reward_data" Substruct="Substruct_JournalRewardData" IfVariableNotSet="complete" />
|
||||
<Data ElementName="unknown6" Type="int8" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_QuestJournalReply" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqQuestJournalReplyCmd" >
|
||||
<Data ElementName="quest_id" Type="int32" Size="1" />
|
||||
|
@ -9084,7 +9084,7 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="response_array" Type="Array" ArraySizeVariable="num_responses">
|
||||
<Data ElementName="response" Type="EQ2_16Bit_String" Size="1" />
|
||||
</Data>
|
||||
<Data ElementName="unknown3" Type="int8" />
|
||||
<Data ElementName="enable_blue_ui" Type="int8" />
|
||||
<Data ElementName="can_close" Type="int8" />
|
||||
<Data ElementName="spawn_id" Type="int32" />
|
||||
<Data ElementName="voice" Type="EQ2_16Bit_String" Size="1" />
|
||||
|
@ -17960,23 +17960,23 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="default_yell_method" Type="int8" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_DefaultGroupOptions" ClientVersion="546" OpcodeName="OP_DefaultGroupOptionsMsg" >
|
||||
<Data ElementName="loot_method" Type="int8" Size="1" />
|
||||
<Data ElementName="loot_items_rarity" Type="int8" Size="1" />
|
||||
<Data ElementName="auto_split_coin" Type="int8" Size="1" />
|
||||
<Data ElementName="default_yell_method" Type="int8" Size="1" />
|
||||
<Data ElementName="group_autolock" Type="int8" Size="1" />
|
||||
<Data ElementName="solo_autolock" Type="int8" Size="1" />
|
||||
<Data ElementName="loot_method" Type="int8" Size="1" /> <!-- 0 = leader, 1 = FFA, 2 = lotto -->
|
||||
<Data ElementName="loot_items_rarity" Type="int8" Size="1" /> <!-- not available in DoF? -->
|
||||
<Data ElementName="auto_split_coin" Type="int8" Size="1" /> <!-- auto split -->
|
||||
<Data ElementName="default_yell_method" Type="int8" Size="1" /> <!-- 0 = leader only, 1 = group allowed -->
|
||||
<Data ElementName="default_group_lock_method" Type="int8" Size="1" /> <!-- 0 = leader, 1 = anyone -->
|
||||
<Data ElementName="group_autolock" Type="int8" Size="1" /> <!-- 0 = false, 1 = true, if set to 0 default_group_lock_method is not sent if changed after -->
|
||||
</Struct>
|
||||
<Struct Name="WS_DefaultGroupOptions" ClientVersion="547" OpcodeName="OP_DefaultGroupOptionsMsg" >
|
||||
<Struct Name="WS_DefaultGroupOptions" ClientVersion="562" OpcodeName="OP_DefaultGroupOptionsMsg" >
|
||||
<Data ElementName="loot_method" Type="int8" Size="1" />
|
||||
<Data ElementName="loot_items_rarity" Type="int8" Size="1" />
|
||||
<Data ElementName="auto_split_coin" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown3" Type="int8" Size="1" />
|
||||
<Data ElementName="default_yell_method" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown5" Type="int8" Size="1" />
|
||||
<Data ElementName="default_group_lock_method" Type="int8" Size="1" />
|
||||
<Data ElementName="group_autolock" Type="int8" Size="1" />
|
||||
<Data ElementName="solo_autolock" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown8" Type="int8" Size="1" />
|
||||
<Data ElementName="auto_loot_method" Type="int8" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_ChoiceWindow" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqChoiceWinCmd">
|
||||
<Data ElementName="text" Type="EQ2_16Bit_String" />
|
||||
|
|
Loading…
Reference in a new issue