gdle/Source/ClientEvents.cpp
2020-03-21 09:23:59 -07:00

4617 lines
124 KiB
C++

#include <StdAfx.h>
#include "Client.h"
#include "ClientCommands.h"
#include "ClientEvents.h"
#include "World.h"
#include <chrono>
#include "Database.h"
#include "DatabaseIO.h"
#include "Database2.h"
#include "ChatMsgs.h"
#include "ObjectMsgs.h"
#include "WeenieObject.h"
#include "Monster.h"
#include "Player.h"
#include "ChatMsgs.h"
#include "Movement.h"
#include "EmoteManager.h"
#include "MovementManager.h"
#include "Vendor.h"
#include "AllegianceManager.h"
#include "House.h"
#include "HouseManager.h"
#include "SpellcastingManager.h"
#include "TradeManager.h"
#include "ChessManager.h"
#include <chrono>
#include "Config.h"
#include "Messages.h"
CClientEvents::CClientEvents(CClient *parent)
{
m_pClient = parent;
m_pPlayer = NULL;
}
CClientEvents::~CClientEvents()
{
if (m_pPlayer)
{
m_pPlayer->BeginLogout();
m_pPlayer->DetachClient();
m_pPlayer = NULL;
}
}
void CClientEvents::DetachPlayer()
{
m_pPlayer = NULL;
}
uint32_t CClientEvents::GetPlayerID()
{
if (!m_pPlayer)
return 0;
return m_pPlayer->GetID();
}
CPlayerWeenie *CClientEvents::GetPlayer()
{
return m_pPlayer;
}
std::string GetAgeString(int age)
{
int mo = age / 2629800; // seconds in a month
int leftover = age % 2629800;
int d = leftover / 86400; // seconds in a day
leftover %= 86400;
int h = leftover / 3600; // seconds in an hour
leftover %= 3600;
int m = leftover / 60; // seconds in a minute
leftover %= 60;
int s = leftover;
std::string out = "You have played for ";
if (mo)
out += csprintf("%dmo ", mo);
if (d)
out += csprintf("%dd ", d);
if (h)
out += csprintf("%dh ", h);
if (m)
out += csprintf("%dm ", m);
out += csprintf("%ds.", s);
return out;
}
void CClientEvents::ExitWorld()
{
auto t = chrono::system_clock::to_time_t(chrono::system_clock::now());
m_pPlayer->m_Qualities.SetInt(LOGOFF_TIMESTAMP_INT, t);
DetachPlayer();
m_pClient->ExitWorld();
}
void CClientEvents::Think()
{
if (m_pPlayer)
{
// update in-game age
int time_now = chrono::system_clock::to_time_t(chrono::system_clock::now());
if (time_now > (last_age_update + 15))
{
int age = m_pPlayer->m_Qualities.GetInt(AGE_INT, 0);
int time_diff = time_now - last_age_update;
age += time_diff;
m_pPlayer->m_Qualities.SetInt(AGE_INT, age);
m_pPlayer->NotifyIntStatUpdated(AGE_INT);
last_age_update = time_now;
}
if (m_bSendAllegianceUpdates)
{
if (m_fNextAllegianceUpdate <= g_pGlobals->Time())
{
SendAllegianceUpdate();
m_fNextAllegianceUpdate = g_pGlobals->Time() + 5.0f;
if (!m_bSentFirstAllegianceUpdate)
{
m_bSendAllegianceUpdates = FALSE;
m_bSentFirstAllegianceUpdate = TRUE;
}
}
}
}
}
void CClientEvents::BeginLogout()
{
if (m_pPlayer && !m_pPlayer->IsLoggingOut())
{
if (m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->NotifyWeenieError(WERROR_ACTIONS_LOCKED);
return;
}
m_pPlayer->BeginLogout();
}
}
void CClientEvents::ForceLogout()
{
if (m_pPlayer && !m_pPlayer->IsLoggingOut())
{
m_pPlayer->BeginLogout();
}
}
void CClientEvents::OnLogoutCompleted()
{
ExitWorld();
}
void CClientEvents::LoginError(int iError)
{
uint32_t ErrorPackage[2];
ErrorPackage[0] = 0xF659;
ErrorPackage[1] = iError;
m_pClient->SendNetMessage(ErrorPackage, sizeof(ErrorPackage), PRIVATE_MSG);
}
void CClientEvents::LoginCharacter(uint32_t char_weenie_id, const char *szAccount)
{
if (!m_pClient->HasCharacter(char_weenie_id))
{
LoginError(13); // update error codes
SERVER_WARN << szAccount << "attempting to log in with a character that doesn't belong to this account";
return;
}
if (m_pPlayer || g_pWorld->FindPlayer(char_weenie_id) || g_pDBIO->GetNumPendingSaves(char_weenie_id) > 0)
{
// LOG(Temp, Normal, "Character already logged in!\n");
LoginError(13); // update error codes
SERVER_WARN << szAccount << "Login request, but character already logged in!";
return;
}
/*
if (_stricmp(szAccount, m_pClient->GetAccountInfo().username))
{
LoginError(15);
LOG(Client, Warning, "Bad account for login: \"%s\" \"%s\"\n", szAccount, m_pClient->GetAccountInfo().username);
return;
}
*/
m_pPlayer = new CPlayerWeenie(m_pClient, char_weenie_id, m_pClient->IncCharacterInstanceTS(char_weenie_id));
if (!m_pPlayer->Load())
{
LoginError(13); // update error codes
SERVER_WARN << szAccount << "Login request, but character failed to load!";
delete m_pPlayer;
return;
}
m_pPlayer->m_Qualities.SetFloat(HEARTBEAT_INTERVAL_FLOAT, 5.0);
m_pPlayer->m_Qualities.SetInt(NETHER_OVER_TIME_INT, 0);
m_pPlayer->m_Qualities.SetInt(DAMAGE_OVER_TIME_INT, 0);
m_pPlayer->m_Qualities.SetFloat(ATTACK_TIMESTAMP_FLOAT, 0.0);
m_pPlayer->LoadTitles();
m_pPlayer->SendTitles();
m_pPlayer->SetLoginPlayerQualities(); // overrides
m_pPlayer->RecalculateEncumbrance();
m_pPlayer->LoginCharacter();
m_pPlayer->SendFullFriendData();
m_pPlayer->LoadSquelches();
m_pPlayer->LoadCorpses();
last_age_update = chrono::system_clock::to_time_t(chrono::system_clock::now());
if (m_pPlayer->m_Qualities._skillStatsTable)
{
// fix for trained skills higher than they should be on char create.
for (uint32_t i = 0; i < m_pPlayer->m_Qualities._skillStatsTable->size(); i++)
{
Skill trained;
m_pPlayer->m_Qualities.InqSkill((STypeSkill)i, trained);
if (trained._sac == SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
// Only do this once.
if (trained._init_level > 0)
{
trained._init_level = 0;
trained._level_from_pp = trained._level_from_pp + 5;
if (trained._level_from_pp > 208)
trained._level_from_pp = 208;
m_pPlayer->m_Qualities.SetSkill((STypeSkill)i, trained);
m_pPlayer->NotifySkillStatUpdated((STypeSkill)i);
}
}
}
}
// give characters created before creation timestamp was being set a timestamp and DOB from their DB date_created
if (!m_pPlayer->m_Qualities.GetInt(CREATION_TIMESTAMP_INT, 0))
{
CharacterDesc_t char_info = g_pDBIO->GetCharacterInfo(m_pPlayer->GetID());
if (char_info.date_created) // check that the query got info, will be 0 if it didn't
{
time_t t = char_info.date_created;
m_pPlayer->m_Qualities.SetInt(CREATION_TIMESTAMP_INT, t);
m_pPlayer->NotifyIntStatUpdated(CREATION_TIMESTAMP_INT);
std::stringstream ss;
ss << "You were born on " << std::put_time(std::localtime(&t), "%m/%d/%y %I:%M:%S %p."); // convert time to a string of format '01/01/18 11:59:59 AM.'
m_pPlayer->m_Qualities.SetString(DATE_OF_BIRTH_STRING, ss.str());
m_pPlayer->NotifyStringStatUpdated(DATE_OF_BIRTH_STRING);
}
}
if (!m_pPlayer->m_Qualities.GetFloat(GLOBAL_XP_MOD_FLOAT, 0.0))
{
m_pPlayer->m_Qualities.SetFloat(GLOBAL_XP_MOD_FLOAT, 1.0);
m_pPlayer->NotifyFloatStatUpdated(GLOBAL_XP_MOD_FLOAT);
}
last_age_update = chrono::system_clock::to_time_t(chrono::system_clock::now());
//temporarily send a purge all enchantments packet on login to wipe stacked characters.
if (g_pConfig->SpellPurgeOnLogin())
{
if (m_pPlayer->m_Qualities._enchantment_reg)
{
PackableList<uint32_t> removed;
if (m_pPlayer->m_Qualities._enchantment_reg->_add_list)
{
for (auto it = m_pPlayer->m_Qualities._enchantment_reg->_add_list->begin(); it != m_pPlayer->m_Qualities._enchantment_reg->_add_list->end();)
{
if (it->_duration == -1.0)
{
removed.push_back(it->_id);
it = m_pPlayer->m_Qualities._enchantment_reg->_add_list->erase(it);
}
else
{
it++;
}
}
}
if (m_pPlayer->m_Qualities._enchantment_reg->_mult_list)
{
for (auto it = m_pPlayer->m_Qualities._enchantment_reg->_mult_list->begin(); it != m_pPlayer->m_Qualities._enchantment_reg->_mult_list->end();)
{
if (it->_duration == -1.0)
{
removed.push_back(it->_id);
it = m_pPlayer->m_Qualities._enchantment_reg->_mult_list->erase(it);
}
else
{
it++;
}
}
}
if (removed.size())
{
// m_Qualities._enchantment_reg->PurgeEnchantments();
BinaryWriter expireMessage;
expireMessage.Write<uint32_t>(0x2C8);
removed.Pack(&expireMessage);
m_pClient->SendNetMessage(&expireMessage, PRIVATE_MSG, TRUE, FALSE);
}
}
}
//Check for enchantments that should not exist
if (m_pPlayer->m_Qualities._enchantment_reg)
{
PackableList<uint32_t> removed;
if (m_pPlayer->m_Qualities._enchantment_reg->_mult_list)
{
for (auto it = m_pPlayer->m_Qualities._enchantment_reg->_mult_list->begin(); it != m_pPlayer->m_Qualities._enchantment_reg->_mult_list->end();)
{
if (it->_id == 48 ||
it->_id == 1635 ||
it->_id == 2645 ||
it->_id == 2647)
{
removed.push_back(it->_id);
it = m_pPlayer->m_Qualities._enchantment_reg->_mult_list->erase(it);
m_pPlayer->SendText("Active enchantment list updated.", LTT_DEFAULT);
}
else
{
it++;
}
}
}
if (removed.size())
{
// m_Qualities._enchantment_reg->PurgeEnchantments();
BinaryWriter expireMessage;
expireMessage.Write<uint32_t>(0x2C8);
removed.Pack(&expireMessage);
m_pClient->SendNetMessage(&expireMessage, PRIVATE_MSG, TRUE, FALSE);
}
}
// give characters created before creation timestamp was being set a timestamp and DOB from their DB date_created
if (!m_pPlayer->m_Qualities.GetInt(CREATION_TIMESTAMP_INT, 0))
{
CharacterDesc_t char_info = g_pDBIO->GetCharacterInfo(m_pPlayer->GetID());
if (char_info.date_created) // check that the query got info, will be 0 if it didn't
{
time_t t = char_info.date_created;
m_pPlayer->m_Qualities.SetInt(CREATION_TIMESTAMP_INT, t);
m_pPlayer->NotifyIntStatUpdated(CREATION_TIMESTAMP_INT);
std::stringstream ss;
ss << std::put_time(std::localtime(&t), "%m/%d/%y %I:%M:%S %p."); // convert time to a string of format '01/01/18 11:59:59 AM.'
m_pPlayer->m_Qualities.SetString(DATE_OF_BIRTH_STRING, ss.str());
m_pPlayer->NotifyStringStatUpdated(DATE_OF_BIRTH_STRING);
}
}
if (m_pPlayer->m_Qualities.GetInt(HERITAGE_GROUP_INT, 0) == Lugian_HeritageGroup)
m_pPlayer->m_Qualities.SetDataID(MOTION_TABLE_DID, 0x9000216);
if (m_pPlayer->m_Qualities.GetInt(HERITAGE_GROUP_INT, 0) == Empyrean_HeritageGroup && m_pPlayer->m_Qualities.GetDID(MOTION_TABLE_DID, 0x9000001) == 0x9000001)
m_pPlayer->m_Qualities.SetDataID(MOTION_TABLE_DID, 0x9000207);
m_pPlayer->SendText("GDLEnhanced " SERVER_VERSION_NUMBER_STRING " " SERVER_VERSION_STRING, LTT_DEFAULT);
m_pPlayer->SendText("Maintained by the GDLE Development Team. Contact us at https://discord.gg/WzGX348", LTT_DEFAULT);
m_pPlayer->SendText("Not an official Asheron's Call server.", LTT_DEFAULT);
if (*g_pConfig->WelcomeMessage() != 0)
{
m_pPlayer->SendText(g_pConfig->WelcomeMessage(), LTT_DEFAULT);
}
SendAllegianceMOTD();
#pragma region RatingsCleanup
if (g_pConfig->RatingPurgeOnLogin())
{
m_pPlayer->m_Qualities.SetInt(DAMAGE_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(DAMAGE_RATING_INT);
m_pPlayer->m_Qualities.SetInt(DAMAGE_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(DAMAGE_RESIST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(CRIT_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(CRIT_RATING_INT);
m_pPlayer->m_Qualities.SetInt(CRIT_DAMAGE_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(CRIT_DAMAGE_RATING_INT);
m_pPlayer->m_Qualities.SetInt(CRIT_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(CRIT_RESIST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(CRIT_DAMAGE_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(CRIT_DAMAGE_RESIST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(HEALING_BOOST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(HEALING_BOOST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(NETHER_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(NETHER_RESIST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(LIFE_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(LIFE_RESIST_RATING_INT);
m_pPlayer->m_Qualities.SetInt(PK_DAMAGE_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(PK_DAMAGE_RATING_INT);
m_pPlayer->m_Qualities.SetInt(PK_DAMAGE_RESIST_RATING_INT, 0);
m_pPlayer->NotifyIntStatUpdated(PK_DAMAGE_RESIST_RATING_INT);
// parse through wielded and set based upon GEAR_RATING
for (auto wielded : m_pPlayer->m_Wielded)
{
STypeInt rating;
int ratingVal = 0;
int gearRating = 0;
for (int i = 370; i < 380; i++)
{
if (wielded->m_Qualities.InqInt((STypeInt)i, gearRating))
{
switch (i)
{
case GEAR_DAMAGE_INT:
rating = DAMAGE_RATING_INT;
break;
case GEAR_DAMAGE_RESIST_INT:
rating = DAMAGE_RESIST_RATING_INT;
break;
case GEAR_CRIT_INT:
rating = CRIT_RATING_INT;
break;
case GEAR_CRIT_RESIST_INT:
rating = CRIT_RESIST_RATING_INT;
break;
case GEAR_CRIT_DAMAGE_INT:
rating = CRIT_DAMAGE_RATING_INT;
break;
case GEAR_CRIT_DAMAGE_RESIST_INT:
rating = CRIT_DAMAGE_RESIST_RATING_INT;
break;
case GEAR_HEALING_BOOST_INT:
rating = HEALING_BOOST_RATING_INT;
break;
case GEAR_NETHER_RESIST_INT:
rating = NETHER_RESIST_RATING_INT;
break;
case GEAR_LIFE_RESIST_INT:
rating = LIFE_RESIST_RATING_INT;
break;
case GEAR_MAX_HEALTH_INT:
rating = GEAR_MAX_HEALTH_INT;
break;
case GEAR_PK_DAMAGE_RATING_INT:
rating = PK_DAMAGE_RATING_INT;
break;
case GEAR_PK_DAMAGE_RESIST_RATING_INT:
rating = PK_DAMAGE_RESIST_RATING_INT;
break;
}
m_pPlayer->m_Qualities.InqInt(rating, ratingVal);
m_pPlayer->m_Qualities.SetInt(rating, ratingVal + gearRating);
m_pPlayer->NotifyIntStatUpdated(rating);
}
}
}
}
#pragma endregion
if (!g_pWorld->CreateEntity(m_pPlayer))
return;
time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
for (auto wielded : m_pPlayer->m_Wielded)
{
// Updates shields to have SHIELD_VALUE_INT
if (wielded->InqIntQuality(ITEM_TYPE_INT, 0) == TYPE_ARMOR && wielded->InqIntQuality(LOCATIONS_INT, 0) == SHIELD_LOC)
{
wielded->m_Qualities.SetInt(SHIELD_VALUE_INT, wielded->InqIntQuality(ARMOR_LEVEL_INT, 0));
}
// Remove all loot items with wcid > g_pConfig->WcidForPurge()
if (g_pConfig->InventoryPurgeOnLogin())
{
if (wielded->m_Qualities.id > g_pConfig->WcidForPurge())
{
wielded->_timeToRot = Timer::cur_time;
wielded->_beganRot = true;
}
}
// Remove all expired items, or set _timeToRot if not yet expired.
int lifespan = wielded->m_Qualities.GetInt(LIFESPAN_INT, 0);
if (lifespan)
{
int creationTime = wielded->m_Qualities.GetInt(CREATION_TIMESTAMP_INT, 0);
int lived = t - creationTime;
if (creationTime && lived >= lifespan)
{
wielded->_timeToRot = Timer::cur_time;
}
else
{
int lifespanRemaining = lifespan - lived;
wielded->_timeToRot = Timer::cur_time + lifespanRemaining;
}
}
}
for (auto item : m_pPlayer->m_Items)
{
// Updates shields to have SHIELD_VALUE_INT
if (item->InqIntQuality(ITEM_TYPE_INT, 0) == TYPE_ARMOR && item->InqIntQuality(LOCATIONS_INT, 0) == SHIELD_LOC)
{
item->m_Qualities.SetInt(SHIELD_VALUE_INT, item->InqIntQuality(ARMOR_LEVEL_INT, 0));
}
if (item->InqIntQuality(ITEM_TYPE_INT, 0) == TYPE_ARMOR && item->InqIntQuality(LOCATIONS_INT, 0) == LOWER_ARM_WEAR_LOC)
{
item->m_Qualities.SetInt(LOCATIONS_INT, LOWER_ARM_ARMOR_LOC);
}
// Remove all loot items with wcid > g_pConfig->WcidForPurge()
if (g_pConfig->InventoryPurgeOnLogin())
{
if (item->m_Qualities.id > g_pConfig->WcidForPurge() && item->m_Qualities.GetInt(ITEM_WORKMANSHIP_INT, 0))
{
item->_timeToRot = Timer::cur_time;
item->_beganRot = true;
}
}
// Remove all expired items, or set _timeToRot if not yet expired.
int lifespan = item->m_Qualities.GetInt(LIFESPAN_INT, 0);
if (lifespan)
{
int creationTime = item->m_Qualities.GetInt(CREATION_TIMESTAMP_INT, 0);
int lived = t - creationTime;
if (creationTime && lived >= lifespan)
{
item->_timeToRot = Timer::cur_time;
}
else
{
int lifespanRemaining = lifespan - lived;
item->_timeToRot = Timer::cur_time + lifespanRemaining;
}
}
}
for (auto pack : m_pPlayer->m_Packs)
{
if (pack->m_Qualities.id != W_PACKCREATUREESSENCE_CLASS && pack->m_Qualities.id != W_PACKITEMESSENCE_CLASS && pack->m_Qualities.id != W_PACKLIFEESSENCE_CLASS &&
pack->m_Qualities.id != W_PACKWARESSENCE_CLASS && pack->m_Qualities.id != 43173)
{
if (pack->AsContainer())
{
for (auto item : pack->AsContainer()->m_Items)
{
// Updates shields to have SHIELD_VALUE_INT
if (item->InqIntQuality(ITEM_TYPE_INT, 0) == TYPE_ARMOR && item->InqIntQuality(LOCATIONS_INT, 0) == SHIELD_LOC)
{
item->m_Qualities.SetInt(SHIELD_VALUE_INT, item->InqIntQuality(ARMOR_LEVEL_INT, 0));
}
if (item->InqIntQuality(ITEM_TYPE_INT, 0) == TYPE_ARMOR && item->InqIntQuality(LOCATIONS_INT, 0) == LOWER_ARM_WEAR_LOC)
{
item->m_Qualities.SetInt(LOCATIONS_INT, LOWER_ARM_ARMOR_LOC);
}
// Remove all loot items with wcid > g_pConfig->WcidForPurge()
if (g_pConfig->InventoryPurgeOnLogin())
{
if (item->m_Qualities.id > g_pConfig->WcidForPurge() && item->m_Qualities.GetInt(ITEM_WORKMANSHIP_INT, 0))
{
item->_timeToRot = Timer::cur_time;
item->_beganRot = true;
}
}
// Remove all expired items, or set _timeToRot if not yet expired.
int lifespan = item->m_Qualities.GetInt(LIFESPAN_INT, 0);
if (lifespan)
{
int creationTime = item->m_Qualities.GetInt(CREATION_TIMESTAMP_INT, 0);
int lived = t - creationTime;
if (creationTime && lived >= lifespan)
{
item->_timeToRot = Timer::cur_time;
}
else
{
int lifespanRemaining = lifespan - lived;
item->_timeToRot = Timer::cur_time + lifespanRemaining;
}
}
}
}
}
}
SendSquelchDB();
//temporarily add all enchantments back from the character's wielded items
if (g_pConfig->SpellPurgeOnLogin())
{
for (auto item : m_pPlayer->m_Wielded)
{
if (item->m_Qualities._spell_book)
{
bool bShouldCast = true;
std::string name;
if (item->m_Qualities.InqString(CRAFTSMAN_NAME_STRING, name))
{
if (!name.empty() && name != item->InqStringQuality(NAME_STRING, ""))
{
bShouldCast = false;
m_pPlayer->NotifyWeenieErrorWithString(WERROR_ACTIVATION_NOT_CRAFTSMAN, name.c_str());
}
}
int difficulty;
difficulty = 0;
if (item->m_Qualities.InqInt(ITEM_DIFFICULTY_INT, difficulty, TRUE, FALSE))
{
uint32_t skillLevel = 0;
if (!m_pPlayer->m_Qualities.InqSkill(ARCANE_LORE_SKILL, skillLevel, FALSE) || (int)skillLevel < difficulty)
{
bShouldCast = false;
m_pPlayer->NotifyWeenieError(WERROR_ACTIVATION_ARCANE_LORE_TOO_LOW);
}
}
if (bShouldCast)
{
difficulty = 0;
uint32_t skillActivationTypeDID = 0;
if (item->m_Qualities.InqInt(ITEM_SKILL_LEVEL_LIMIT_INT, difficulty, TRUE, FALSE) && item->m_Qualities.InqDataID(ITEM_SKILL_LIMIT_DID, skillActivationTypeDID))
{
STypeSkill skillActivationType = SkillTable::OldToNewSkill((STypeSkill)skillActivationTypeDID);
uint32_t skillLevel = 0;
if (!m_pPlayer->m_Qualities.InqSkill(skillActivationType, skillLevel, FALSE) || (int)skillLevel < difficulty)
{
bShouldCast = false;
m_pPlayer->NotifyWeenieErrorWithString(WERROR_ACTIVATION_SKILL_TOO_LOW, CachedSkillTable->GetSkillName(skillActivationType).c_str());
}
}
}
if (bShouldCast && item->InqIntQuality(ITEM_ALLEGIANCE_RANK_LIMIT_INT, 0) > item->InqIntQuality(ALLEGIANCE_RANK_INT, 0))
{
bShouldCast = false;
m_pPlayer->NotifyInventoryFailedEvent(item->GetID(), WERROR_ACTIVATION_RANK_TOO_LOW);
}
if (bShouldCast)
{
int heritageRequirement = item->InqIntQuality(HERITAGE_GROUP_INT, -1);
if (heritageRequirement != -1 && heritageRequirement != item->InqIntQuality(HERITAGE_GROUP_INT, 0))
{
bShouldCast = false;
std::string heritageString = item->InqStringQuality(ITEM_HERITAGE_GROUP_RESTRICTION_STRING, "of the correct heritage");
m_pPlayer->NotifyWeenieErrorWithString(WERROR_ACTIVATION_WRONG_RACE, heritageString.c_str());
}
}
int currentMana = 0;
if (bShouldCast && item->m_Qualities.InqInt(ITEM_CUR_MANA_INT, currentMana, TRUE, FALSE))
{
if (currentMana == 0)
{
bShouldCast = false;
m_pPlayer->NotifyWeenieError(WERROR_ACTIVATION_NOT_ENOUGH_MANA);
}
else
item->_nextManaUse = Timer::cur_time;
}
if (bShouldCast)
{
uint32_t serial = 0;
serial |= ((uint32_t)m_pPlayer->GetEnchantmentSerialByteForMask(item->InqIntQuality(LOCATIONS_INT, 0, TRUE)) << (uint32_t)0);
serial |= ((uint32_t)m_pPlayer->GetEnchantmentSerialByteForMask(item->InqIntQuality(CLOTHING_PRIORITY_INT, 0, TRUE)) << (uint32_t)8);
for (auto &spellPage : item->m_Qualities._spell_book->_spellbook)
{
item->MakeSpellcastingManager()->CastSpellEquipped(m_pPlayer->GetID(), spellPage.first, (WORD)serial);
}
}
}
}
}
m_pPlayer->DebugValidate();
// 1.32 Skill Reset Grant
std::string resetSkillQuest132 = "ResetFreeSkillQuest132";
bool resetGiven = m_pPlayer->_questTable.HasQuest(resetSkillQuest132.c_str());
int freeUsed = m_pPlayer->_questTable.InqQuestSolves("UsedFreeSkillReset");
if (!resetGiven && freeUsed == 1)
{
m_pPlayer->_questTable.RemoveQuest("UsedFreeSkillReset");
m_pPlayer->_questTable.AddQuest(resetSkillQuest132.c_str());
BinaryWriter popupMessage;
popupMessage.Write<uint32_t>(0x4);
popupMessage.WriteString("You have been granted a free skill reset.\nVisit Asheron's Castle to reset skills.");
m_pPlayer->SendNetMessage(&popupMessage, PRIVATE_MSG, TRUE, FALSE);
}
}
void CClientEvents::SendSquelchDB()
{
BinaryWriter setSquelchDB;
setSquelchDB.Write<uint32_t>(0x01F4u);
setSquelchDB.Write<WORD>(0); // Number of accounts squelched - NYI
setSquelchDB.Write<WORD>(0); // Size of ints for above
std::list<CharacterSquelch_t> squelches = m_pPlayer->GetSquelches();
setSquelchDB.Write<WORD>(squelches.size());
setSquelchDB.Write<WORD>(32); // Size of ints for above
for (std::list<CharacterSquelch_t>::iterator it = squelches.begin(); it != squelches.end(); ++it)
{
setSquelchDB.Write<uint32_t>(it->squelched_id);
if (it->account_id > 0)
{
setSquelchDB.Write<uint32_t>(4);
setSquelchDB.Write<uint32_t>(SquelchMasks::AllChannels_Mask);
setSquelchDB.Write<uint32_t>(SquelchMasks::AllChannels_Mask);
setSquelchDB.Write<uint32_t>(SquelchMasks::AllChannels_Mask);
setSquelchDB.Write<uint32_t>(SquelchMasks::AllChannels_Mask);
}
setSquelchDB.WriteString(g_pWorld->GetPlayerName(it->squelched_id, true));
setSquelchDB.Write<uint32_t>(1);
}
setSquelchDB.Write<WORD>(0); // Number of global squelches - NYI
setSquelchDB.Write<WORD>(0); // Size of ints for above
setSquelchDB.Write<uint32_t>(0); // Number of globals squelched - NYI
setSquelchDB.Write<uint32_t>(0); // Number of accounts squelched - NYI
m_pClient->SendNetMessage(&setSquelchDB, PRIVATE_MSG, TRUE, FALSE);
}
void CClientEvents::SendText(const char *szText, int32_t lColor)
{
m_pClient->SendNetMessage(ServerText(szText, lColor), PRIVATE_MSG, FALSE, TRUE);
}
void CClientEvents::Attack(uint32_t target, uint32_t height, float power)
{
if (height <= 0 || height >= ATTACK_HEIGHT::NUM_ATTACK_HEIGHTS)
{
SERVER_WARN << "Bad melee attack height" << height << "sent by player" << m_pPlayer->GetID();
return;
}
if (power < 0.0f || power > 1.0f)
{
SERVER_WARN << "Bad melee attack power" << power << "sent by player" << m_pPlayer->GetID();
return;
}
m_pPlayer->TryMeleeAttack(target, (ATTACK_HEIGHT)height, power);
}
void CClientEvents::MissileAttack(uint32_t target, uint32_t height, float power)
{
if (height <= 0 || height >= ATTACK_HEIGHT::NUM_ATTACK_HEIGHTS)
{
SERVER_WARN << "Bad missile attack height" << height << "sent by player" << m_pPlayer->GetID();
return;
}
if (power < 0.0f || power > 1.0f)
{
SERVER_WARN << "Bad missile attack power" << power << "sent by player" << m_pPlayer->GetID();
return;
}
m_pPlayer->TryMissileAttack(target, (ATTACK_HEIGHT)height, power);
}
void CClientEvents::SendTell(const char* szText, const char* targetName, const uint32_t targetId)
{
CPlayerWeenie *pTarget;
if (targetId > 0) // From SendTellByGuid
{
if (!(pTarget = g_pWorld->FindPlayer(targetId)))
{
CWeenieObject *target = g_pWorld->FindObject(targetId);
if (target && target->m_Qualities._emote_table && !target->m_Qualities._emote_table->_emote_table.empty()) {
char szResponse[300];
snprintf(szResponse, 300, "You tell %s, \"%s\"", target->GetName().c_str(), szText);
m_pPlayer->SendNetMessage(ServerText(szResponse, 4), PRIVATE_MSG, FALSE, TRUE);
target->MakeEmoteManager()->ChanceExecuteEmoteSet(ReceiveTalkDirect_EmoteCategory, szText, m_pPlayer->GetID());
}
return;
}
}
else
{
if (!(pTarget = g_pWorld->FindPlayer(targetName)))
return;
}
if (strlen(szText) > 300)
return;
if (pTarget->GetID() == m_pPlayer->GetID())
{
std::string filteredText = FilterBadChatCharacters(szText);
char szResponse[300];
snprintf(szResponse, 300, "You think, \"%s\"", szText);
m_pPlayer->SendNetMessage(ServerText(szResponse, 3), PRIVATE_MSG, FALSE, TRUE);
return;
}
if (!CheckForChatSpam())
return;
if (!pTarget->IsPlayerSquelched(m_pPlayer->GetID(), true))
{
std::string filteredText = FilterBadChatCharacters(szText);
char szResponse[300];
snprintf(szResponse, 300, "You tell %s, \"%s\"", pTarget->GetName().c_str(), szText);
m_pPlayer->SendNetMessage(ServerText(szResponse, 4), PRIVATE_MSG, FALSE, TRUE);
pTarget->SendNetMessage(DirectChat(szText, m_pPlayer->GetName().c_str(), m_pPlayer->GetID(), pTarget->GetID(), 3), PRIVATE_MSG, TRUE);
}
}
void CClientEvents::ClientText(const char *szText)
{
if (strlen(szText) > 500)
return;
//should really check for invalid characters and such ;]
while (szText[0] == ' ') //Skip leading spaces.
szText++;
if (szText[0] == '\0') //Make sure the text isn't blank
return;
std::string filteredText = m_pClient->GetAccessLevel() >= SENTINEL_ACCESS ? szText : FilterBadChatCharacters(szText);
szText = filteredText.c_str(); // hacky
if (szText[0] == '@' || szText[0] == '/')
{
CommandBase::Execute((char *)(++szText), m_pClient);
}
else
{
if (CheckForChatSpam())
{
m_pPlayer->SpeakLocal(szText);
}
}
}
void CClientEvents::EmoteText(const char* szText)
{
if (strlen(szText) > 300)
return;
//TODO: Check for invalid characters and such ;)
while (szText[0] == ' ') //Skip leading spaces.
szText++;
if (szText[0] == '\0') //Make sure the text isn't blank
return;
m_pPlayer->EmoteLocal(szText);
}
void CClientEvents::ActionText(const char *text)
{
if (strlen(text) > 300)
return;
while (text[0] == ' ') //Skip leading spaces.
text++;
if (text[0] == '\0') //Make sure the text isn't blank
return;
m_pPlayer->ActionLocal(text);
}
void CClientEvents::ChannelText(uint32_t channel_id, const char *text)
{
if (strlen(text) > 300)
return;
// TODO: check for invalid characters and such
while (text[0] == ' ')
text++;
if (text[0] == '\0')
return;
switch (channel_id)
{
case Fellow_ChannelID:
{
if (m_pPlayer->HasFellowship())
{
g_pFellowshipManager->Chat(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), text);
CHAT_LOG << m_pPlayer->GetName().c_str() << "says (fellowship)," << text;
}
break;
}
case Patron_ChannelID:
g_pAllegianceManager->ChatPatron(m_pPlayer->GetID(), text);
CHAT_LOG << m_pPlayer->GetName().c_str() << "says (patron)," << text;
break;
case Vassals_ChannelID:
g_pAllegianceManager->ChatVassals(m_pPlayer->GetID(), text);
CHAT_LOG << m_pPlayer->GetName().c_str() << "says (vassals)," << text;
break;
case Covassals_ChannelID:
g_pAllegianceManager->ChatCovassals(m_pPlayer->GetID(), text);
CHAT_LOG << m_pPlayer->GetName().c_str() << "says (covassals)," << text;
break;
case Monarch_ChannelID:
g_pAllegianceManager->ChatMonarch(m_pPlayer->GetID(), text);
CHAT_LOG << m_pPlayer->GetName().c_str() << "says (monarch)," << text;
break;
}
}
void CClientEvents::RequestHealthUpdate(uint32_t dwGUID)
{
CWeenieObject *pEntity = g_pWorld->FindWithinPVS(m_pPlayer, dwGUID);
if (pEntity)
{
if (pEntity->IsCreature())
{
m_pPlayer->SetLastHealthRequest(pEntity->GetID());
m_pClient->SendNetMessage(HealthUpdate((CMonsterWeenie *)pEntity), PRIVATE_MSG, TRUE, TRUE);
}
}
}
void CClientEvents::ChangeCombatStance(COMBAT_MODE mode)
{
m_pPlayer->ChangeCombatMode(mode, true);
// ActionComplete();
}
void CClientEvents::ExitPortal()
{
m_pPlayer->ExitPortal();
}
void CClientEvents::Ping()
{
// Pong!
uint32_t Pong = 0x1EA;
m_pClient->SendNetMessage(&Pong, sizeof(Pong), PRIVATE_MSG, TRUE);
}
void CClientEvents::UseItemEx(uint32_t dwSourceID, uint32_t dwDestID)
{
CWeenieObject *pSource = g_pWorld->FindWithinPVS(m_pPlayer, dwSourceID);
CWeenieObject *pDest = g_pWorld->FindWithinPVS(m_pPlayer, dwDestID);
if (pSource && pSource->AsCaster())
m_pPlayer->ExecuteUseEvent(new CWandSpellUseEvent(dwSourceID, pDest ? dwDestID : dwSourceID));
else if (pSource && pDest)
pSource->UseWith(m_pPlayer, pDest);
else
ActionComplete();
}
void CClientEvents::UseObject(uint32_t dwEID)
{
if (m_pPlayer->IsBusyOrInAction())
{
ActionComplete(WERROR_ACTIONS_LOCKED);
return;
}
CWeenieObject *pTarget = g_pWorld->FindWithinPVS(m_pPlayer, dwEID);
if (pTarget)
{
if (pTarget->AsContainer() && pTarget->AsContainer()->_openedById == m_pPlayer->GetID())
{
//we're closing a chest
pTarget->AsContainer()->OnContainerClosed(m_pPlayer);
ActionComplete();
return;
}
int error = pTarget->UseChecked(m_pPlayer);
if (error != WERROR_NONE)
ActionComplete(error);
}
else
{
ActionComplete(WERROR_OBJECT_GONE);
}
}
void CClientEvents::ActionComplete(int error)
{
if (!m_pPlayer)
return;
m_pPlayer->NotifyUseDone(error);
}
void CClientEvents::Identify(uint32_t target_id)
{
//if (_next_allowed_identify > Timer::cur_time)
//{
// // do not allow to ID too fast
// return;
//}
/*
CWeenieObject *pTarget = g_pWorld->FindWithinPVS(m_pPlayer, target_id);
if (!pTarget)
{
// used to check for vendor items, temporary, should be changed
pTarget = g_pWorld->FindObject(target_id);
}
*/
CWeenieObject *pTarget = g_pWorld->FindObject(target_id);
#ifndef PUBLIC_BUILD
if (pTarget)
#else
int vis = 0;
if (pTarget && !pTarget->m_Qualities.InqBool(VISIBILITY_BOOL, vis))
#endif
{
pTarget->TryIdentify(m_pPlayer);
m_pPlayer->SetLastAssessed(pTarget->GetID());
}
//_next_allowed_identify = Timer::cur_time + 0.5;
}
void CClientEvents::SpendAttributeXP(STypeAttribute key, uint32_t exp)
{
// TODO use attribute map
if (key < 1 || key > 6)
return;
// TODO verify they are trying to spend the correct amount of XP
int64_t unassignedExp = 0;
m_pPlayer->m_Qualities.InqInt64(AVAILABLE_EXPERIENCE_INT64, unassignedExp);
if ((uint64_t)unassignedExp < (uint64_t)exp)
{
// Not enough experience
return;
}
Attribute attr;
if (!m_pPlayer->m_Qualities.InqAttribute(key, attr))
{
// Doesn't have the attribute
return;
}
const uint32_t amountNeededForMaxXp = attr.GetXpNeededForMaxXp();
exp = min(exp, amountNeededForMaxXp);
if (exp > 0)
{
m_pPlayer->GiveAttributeXP(key, exp);
m_pPlayer->m_Qualities.SetInt64(AVAILABLE_EXPERIENCE_INT64, (uint64_t)unassignedExp - (uint64_t)exp);
m_pPlayer->NotifyAttributeStatUpdated(key);
m_pPlayer->NotifyInt64StatUpdated(AVAILABLE_EXPERIENCE_INT64);
}
}
void CClientEvents::SpendAttribute2ndXP(STypeAttribute2nd key, uint32_t exp)
{
// TODO use vital map
if (key != 1 && key != 3 && key != 5)
return;
// TODO verify they are trying to spend the correct amount of XP
__int64 unassignedExp = 0;
m_pPlayer->m_Qualities.InqInt64(AVAILABLE_EXPERIENCE_INT64, unassignedExp);
if ((uint64_t)unassignedExp < (uint64_t)exp)
{
// Not enough experience
return;
}
SecondaryAttribute attr;
if (!m_pPlayer->m_Qualities.InqAttribute2nd(key, attr))
{
// Doesn't have the secondary attribute
return;
}
const uint32_t amountNeededForMaxXp = attr.GetXpNeededForMaxXp();
// If the exp is more than is needed to reach max, it is limited to the amount needed to reach max
// This is done as the client may send more than the amount needed if it is desynced
exp = min(exp, amountNeededForMaxXp);
if (exp > 0)
{
m_pPlayer->GiveAttribute2ndXP(key, exp);
m_pPlayer->m_Qualities.SetInt64(AVAILABLE_EXPERIENCE_INT64, (uint64_t)unassignedExp - (uint64_t)exp);
m_pPlayer->NotifyInt64StatUpdated(AVAILABLE_EXPERIENCE_INT64);
}
fellowship_ptr_t fs = m_pPlayer->GetFellowship();
if (fs)
{
fs->StatsUpdate(m_pPlayer->id);
}
}
void CClientEvents::SpendSkillXP(STypeSkill key, uint32_t exp)
{
// TODO verify they are trying to spend the correct amount of XP
int64_t unassignedExp = 0;
m_pPlayer->m_Qualities.InqInt64(AVAILABLE_EXPERIENCE_INT64, unassignedExp);
if ((uint64_t)unassignedExp < (uint64_t)exp)
{
// Not enough experience
return;
}
Skill skill;
if (!m_pPlayer->m_Qualities.InqSkill(key, skill) || skill._sac < TRAINED_SKILL_ADVANCEMENT_CLASS)
{
// Skill doesn't exist or isn't trained
return;
}
const uint32_t amountNeededForMaxXp = skill.GetXpNeededForMaxXp();
// If the exp is more than is needed to reach max, it is limited to the amount needed to reach max
// This is done as the client may send more than the amount needed if it is desynced
exp = min(exp, amountNeededForMaxXp);
if (exp > 0)
{
// Only give the skill exp if it's not maxed and only take exp if GiveSkillXP does not return 0
if (m_pPlayer->GiveSkillXP(key, exp))
{
m_pPlayer->m_Qualities.SetInt64(AVAILABLE_EXPERIENCE_INT64, (uint64_t)unassignedExp - (uint64_t)exp);
m_pPlayer->NotifyInt64StatUpdated(AVAILABLE_EXPERIENCE_INT64);
}
}
}
void CClientEvents::SpendSkillCredits(STypeSkill key, uint32_t credits)
{
// TODO verify they are trying to spend the correct amount of XP
uint32_t unassignedCredits = 0;
m_pPlayer->m_Qualities.InqInt(AVAILABLE_SKILL_CREDITS_INT, *(int *)&unassignedCredits);
if (unassignedCredits < credits)
{
// Not enough experience
return;
}
Skill skill;
if (!m_pPlayer->m_Qualities.InqSkill(key, skill) || skill._sac >= TRAINED_SKILL_ADVANCEMENT_CLASS)
{
// Skill doesn't exist or already trained
return;
}
uint32_t costToRaise = m_pPlayer->GetCostToRaiseSkill(key);
if (m_pPlayer->GetCostToRaiseSkill(key) != credits)
{
SERVER_WARN << m_pPlayer->GetName() << "- Credit cost to raise skill does not match what player is trying to spend.";
return;
}
m_pPlayer->GiveSkillAdvancementClass(key, TRAINED_SKILL_ADVANCEMENT_CLASS);
m_pPlayer->m_Qualities._skillStatsTable->operator[](key)._level_from_pp = 5;
m_pPlayer->m_Qualities._skillStatsTable->operator[](key)._pp = 526;
m_pPlayer->NotifySkillStatUpdated(key);
m_pPlayer->m_Qualities.SetInt(AVAILABLE_SKILL_CREDITS_INT, unassignedCredits - costToRaise);
m_pPlayer->NotifyIntStatUpdated(AVAILABLE_SKILL_CREDITS_INT);
}
void CClientEvents::LifestoneRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
Position lifestone;
if (m_pPlayer->m_Qualities.InqPosition(SANCTUARY_POSITION, lifestone) && lifestone.objcell_id)
{
if (!m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->ExecuteUseEvent(new CLifestoneRecallUseEvent());
}
}
else
{
m_pClient->SendNetMessage(ServerText("You are not bound to a Lifestone!", 7), PRIVATE_MSG);
}
}
void CClientEvents::MarketplaceRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
if (!m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->ExecuteUseEvent(new CMarketplaceRecallUseEvent());
}
}
void CClientEvents::PKArenaRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
if (!m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->ExecuteUseEvent(new CPKArenaUseEvent());
}
}
void CClientEvents::PKLArenaRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
if (!m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->ExecuteUseEvent(new CPKLArenaUseEvent());
}
}
void CClientEvents::TryInscribeItem(uint32_t object_id, const std::string &text)
{
CWeenieObject *weenie = m_pPlayer->FindContainedItem(object_id);
if (!weenie)
{
return;
}
if (!weenie->IsInscribable())
{
return;
}
weenie->m_Qualities.SetString(INSCRIPTION_STRING, text.length() <= 800 ? text : text.substr(0, 800));
weenie->m_Qualities.SetString(SCRIBE_NAME_STRING, m_pPlayer->GetName());
}
void CClientEvents::TryBuyItems(uint32_t vendor_id, std::list<class ItemProfile *> &items)
{
CWeenieObject *weenie = g_pWorld->FindWithinPVS(m_pPlayer, vendor_id);
if (!weenie)
{
ActionComplete(WERROR_NO_OBJECT);
return;
}
int error = WERROR_NO_OBJECT;
if (weenie->IsVendor())
{
error = ((CVendor *)weenie)->TrySellItemsToPlayer(m_pPlayer, items);
((CVendor *)weenie)->SendVendorInventory(m_pPlayer);
}
ActionComplete(error);
}
void CClientEvents::TrySellItems(uint32_t vendor_id, std::list<class ItemProfile *> &items)
{
CWeenieObject *weenie = g_pWorld->FindWithinPVS(m_pPlayer, vendor_id);
if (!weenie)
{
ActionComplete(WERROR_NO_OBJECT);
return;
}
int error = WERROR_NO_OBJECT;
if (weenie->IsVendor())
{
error = ((CVendor *)weenie)->TryBuyItemsFromPlayer(m_pPlayer, items);
((CVendor *)weenie)->SendVendorInventory(m_pPlayer);
}
ActionComplete(error);
}
void CClientEvents::TryFellowshipCreate(const std::string name, int shareXP)
{
if (m_pPlayer->HasFellowship())
return;
int error = g_pFellowshipManager->Create(name, m_pPlayer->GetID(), shareXP);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
void CClientEvents::TryFellowshipQuit(int disband)
{
if (m_pPlayer->HasFellowship())
{
// ensure we have a copy for this since it could be the last member
fellowship_ptr_t fellowship = m_pPlayer->GetFellowship();
int error = 0;
if (disband)
error = g_pFellowshipManager->Disband(fellowship, m_pPlayer->GetID());
else
error = g_pFellowshipManager->Quit(fellowship, m_pPlayer->GetID());
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::TryFellowshipDismiss(uint32_t dismissed)
{
if (m_pPlayer->HasFellowship())
{
int error = g_pFellowshipManager->Dismiss(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), dismissed);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::TryFellowshipRecruit(uint32_t target)
{
if (m_pPlayer->HasFellowship())
{
int error = g_pFellowshipManager->Recruit(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), target);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::TryFellowshipUpdate(int on)
{
if (m_pPlayer->HasFellowship())
{
int error = g_pFellowshipManager->RequestUpdates(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), on);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::TryFellowshipAssignNewLeader(uint32_t target)
{
if (m_pPlayer->HasFellowship())
{
int error = g_pFellowshipManager->AssignNewLeader(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), target);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::TryFellowshipChangeOpenness(int open)
{
if (m_pPlayer->HasFellowship())
{
int error = g_pFellowshipManager->ChangeOpen(m_pPlayer->GetFellowship(), m_pPlayer->GetID(), open);
if (error)
m_pPlayer->NotifyWeenieError(error);
}
}
void CClientEvents::SendAllegianceUpdate()
{
if (!m_pPlayer)
return;
g_pAllegianceManager->SendAllegianceProfile(m_pPlayer);
}
void CClientEvents::SendAllegianceMOTD()
{
g_pAllegianceManager->LoginMOTD(m_pPlayer);
}
void CClientEvents::SetRequestAllegianceUpdate(bool on)
{
m_bSendAllegianceUpdates = on;
}
void CClientEvents::TryBreakAllegiance(uint32_t target)
{
g_pAllegianceManager->TryBreakAllegiance(m_pPlayer, target);
}
void CClientEvents::TrySwearAllegiance(uint32_t target)
{
CPlayerWeenie *targetWeenie = g_pWorld->FindPlayer(target);
if (!targetWeenie)
{
m_pPlayer->NotifyWeenieError(WERROR_NO_OBJECT);
return;
}
g_pAllegianceManager->TrySwearAllegiance(m_pPlayer, targetWeenie);
}
void CClientEvents::AllegianceInfoRequest(const std::string &target)
{
AllegianceTreeNode *myNode = g_pAllegianceManager->GetTreeNode(m_pPlayer->GetID());
if (!myNode)
{
m_pPlayer->SendText("You are not in an allegiance.", LTT_DEFAULT);
return;
}
AllegianceTreeNode *myMonarchNode = g_pAllegianceManager->GetTreeNode(myNode->_monarchID);
if (!myMonarchNode)
{
m_pPlayer->SendText("There was an error processing your request.", LTT_DEFAULT);
return;
}
AllegianceTreeNode *myTargetNode = myMonarchNode->FindCharByNameRecursivelySlow(target);
if (!myTargetNode)
{
m_pPlayer->SendText("Could not find allegiance member.", LTT_DEFAULT);
return;
}
unsigned int rank = 0;
if (AllegianceProfile *profile = g_pAllegianceManager->CreateAllegianceProfile(myTargetNode->_charID, &rank))
{
BinaryWriter allegianceUpdate;
allegianceUpdate.Write<uint32_t>(0x27C);
allegianceUpdate.Write<uint32_t>(myTargetNode->_charID);
profile->Pack(&allegianceUpdate);
m_pPlayer->SendNetMessage(&allegianceUpdate, PRIVATE_MSG, TRUE, FALSE);
delete profile;
}
else
{
m_pPlayer->SendText("Error retrieving allegiance member information.", LTT_DEFAULT);
}
}
void CClientEvents::TrySetAllegianceMOTD(const std::string &text)
{
AllegianceTreeNode *self = g_pAllegianceManager->GetTreeNode(m_pPlayer->GetID());
if (!self)
return;
if (self->_charID != self->_monarchID)
{
m_pPlayer->SendText("Only the monarch can set the Message of the Day.", LTT_DEFAULT);
return;
}
AllegianceInfo *info = g_pAllegianceManager->GetInfo(self->_monarchID);
if (!info)
return;
info->_info.m_motd = text;
info->_info.m_motdSetBy = m_pPlayer->GetName();
m_pPlayer->SendText(csprintf("MOTD changed to: \"%s\" -- %s", info->_info.m_motd.c_str(), info->_info.m_motdSetBy.c_str()), LTT_DEFAULT);
}
void CClientEvents::AllegianceHometownRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
AllegianceTreeNode *allegianceNode = g_pAllegianceManager->GetTreeNode(m_pPlayer->GetID());
if (!allegianceNode)
{
m_pPlayer->NotifyWeenieError(WERROR_ALLEGIANCE_NONEXISTENT);
return;
}
AllegianceInfo *allegianceInfo = g_pAllegianceManager->GetInfo(allegianceNode->_monarchID);
if (allegianceInfo && allegianceInfo->_info.m_BindPoint.objcell_id)
{
if (!m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->ExecuteUseEvent(new CAllegianceHometownRecallUseEvent());
}
}
else
m_pPlayer->NotifyWeenieError(WERROR_ALLEGIANCE_HOMETOWN_NOT_SET);
}
void CClientEvents::HouseBuy(uint32_t slumlord, const PackableList<uint32_t> &items)
{
if (CWeenieObject *slumlordObj = g_pWorld->FindObject(slumlord))
{
if (m_pPlayer->DistanceTo(slumlordObj, true) > 10.0)
return;
if (CSlumLordWeenie *slumlord = slumlordObj->AsSlumLord())
{
slumlord->BuyHouse(m_pPlayer, items);
}
}
}
void CClientEvents::HouseRent(uint32_t slumlord, const PackableList<uint32_t> &items)
{
if (CWeenieObject *slumlordObj = g_pWorld->FindObject(slumlord))
{
if (m_pPlayer->DistanceTo(slumlordObj, true) > 10.0)
return;
if (CSlumLordWeenie *slumlord = slumlordObj->AsSlumLord())
{
slumlord->RentHouse(m_pPlayer, items);
}
}
}
void CClientEvents::HouseAbandon()
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData && houseData->_ownerId == m_pPlayer->GetID())
{
houseData->AbandonHouse();
m_pPlayer->SendText("You've abandoned your house.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("That house does not belong to you.", LTT_DEFAULT); //todo: made up message.
}
else
m_pClient->SendNetMessage(ServerText("You do not have a house!", 7), PRIVATE_MSG);
}
void CClientEvents::HouseRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
uint32_t houseId = m_pPlayer->GetAccountHouseId();
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (m_pPlayer->GetClient() && houseData->_ownerAccount == m_pPlayer->GetClient()->GetAccountInfo().id)
{
if (!m_pPlayer->IsBusyOrInAction())
m_pPlayer->ExecuteUseEvent(new CHouseRecallUseEvent());
}
else
m_pPlayer->SendText("That house does not belong to you.", LTT_DEFAULT); //todo: made up message.
}
else
m_pClient->SendNetMessage(ServerText("You do not have a house!", 7), PRIVATE_MSG);
}
void CClientEvents::HouseMansionRecall()
{
if (m_pPlayer->CheckPKActivity())
{
m_pPlayer->SendText("You have been involved in Player Killer combat too recently!", LTT_MAGIC);
return;
}
if (m_pPlayer->InqBoolQuality(RECALLS_DISABLED_BOOL, 0))
{
m_pPlayer->NotifyWeenieError(WERROR_PORTAL_RECALLS_DISABLED);
return;
}
AllegianceTreeNode *allegianceNode = g_pAllegianceManager->GetTreeNode(m_pPlayer->GetID());
if (!allegianceNode)
{
m_pPlayer->NotifyWeenieError(WERROR_ALLEGIANCE_NONEXISTENT);
return;
}
uint32_t allegianceHouseId;
CWeenieObject *monarch = g_pWorld->FindObject(allegianceNode->_monarchID);
if (!monarch)
{
monarch = CWeenieObject::Load(allegianceNode->_monarchID);
if (!monarch)
return;
allegianceHouseId = monarch->InqDIDQuality(HOUSEID_DID, 0);
delete monarch;
}
else
allegianceHouseId = monarch->InqDIDQuality(HOUSEID_DID, 0);
if (allegianceHouseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(allegianceHouseId);
if (houseData && houseData->_ownerId == allegianceNode->_monarchID && (houseData->_houseType == Villa_HouseType || houseData->_houseType == Mansion_HouseType)) //2 = villa, 3 = mansion
{
if (!m_pPlayer->IsBusyOrInAction())
m_pPlayer->ExecuteUseEvent(new CMansionRecallUseEvent());
return;
}
}
if (allegianceNode->_patronID)
m_pClient->SendNetMessage(ServerText("Your monarch does not own a mansion or villa!", 7), PRIVATE_MSG);
else
m_pClient->SendNetMessage(ServerText("You do not own a mansion or villa!", 7), PRIVATE_MSG);
}
void CClientEvents::HouseRequestData()
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
g_pHouseManager->SendHouseData(m_pPlayer, houseId);
else
{
//if we can't get the data send the "no house" packet
BinaryWriter noHouseData;
noHouseData.Write<uint32_t>(0x0226);
noHouseData.Write<uint32_t>(0);
m_pPlayer->SendNetMessage(&noHouseData, PRIVATE_MSG, TRUE, FALSE);
}
}
void CClientEvents::HouseToggleHooks(bool newSetting)
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
houseData->SetHookVisibility(newSetting);
if (newSetting)
m_pPlayer->SendText("Your dwelling's hooks are now visible.", LTT_DEFAULT); //todo: made up message.
else
m_pPlayer->SendText("Your dwelling's hooks are now hidden.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseRequestAccessList()
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
m_pPlayer->SendText("Access:", LTT_DEFAULT);
m_pPlayer->SendText(csprintf(" Public: %s", houseData->_everyoneAccess ? "Allow" : "Deny"), LTT_DEFAULT);
m_pPlayer->SendText(csprintf(" Allegiance: %s", houseData->_allegianceAccess ? "Allow" : "Deny"), LTT_DEFAULT);
m_pPlayer->SendText("Storage:", LTT_DEFAULT);
m_pPlayer->SendText(csprintf(" Public: %s", houseData->_everyoneStorageAccess ? "Allow" : "Deny"), LTT_DEFAULT);
m_pPlayer->SendText(csprintf(" Allegiance: %s", houseData->_allegianceStorageAccess ? "Allow" : "Deny"), LTT_DEFAULT);
if (houseData->_accessList.empty())
m_pPlayer->SendText("Your dwelling's acess list is empty.", LTT_DEFAULT);
else
{
m_pPlayer->SendText("Access List:", LTT_DEFAULT);
std::list<uint32_t>::iterator i = houseData->_accessList.begin();
while (i != houseData->_accessList.end())
{
std::string name = g_pWorld->GetPlayerName(*(i), true);
if (!name.empty())
{
m_pPlayer->SendText(csprintf(" %s", name.c_str()), LTT_DEFAULT);
i++;
}
else
{
i = houseData->_accessList.erase(i); //no longer exists.
houseData->Save();
}
}
}
if (houseData->_storageAccessList.empty())
m_pPlayer->SendText("Your dwelling's storage access list is empty.", LTT_DEFAULT);
else
{
m_pPlayer->SendText("Storage Access list:", LTT_DEFAULT);
std::list<uint32_t>::iterator i = houseData->_storageAccessList.begin();
while (i != houseData->_storageAccessList.end())
{
std::string name = g_pWorld->GetPlayerName(*(i), true);
if (!name.empty())
{
m_pPlayer->SendText(csprintf(" %s", name.c_str()), LTT_DEFAULT);
i++;
}
else
{
i = houseData->_storageAccessList.erase(i); //no longer exists.
houseData->Save();
}
}
}
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseAddPersonToAccessList(std::string name)
{
uint32_t targetId = g_pWorld->GetPlayerId(name.c_str(), true);
if (!target)
{
m_pPlayer->SendText("Can't find a player by that name.", LTT_DEFAULT); //todo: made up message.
return;
}
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (houseData->_accessList.size() > 128)
{
m_pPlayer->SendText("The access list is full", LTT_DEFAULT); //todo: made up message.
return;
}
if (std::find(houseData->_accessList.begin(), houseData->_accessList.end(), targetId) != houseData->_accessList.end())
{
m_pPlayer->SendText(csprintf("%s is already in the access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
}
else
{
m_pPlayer->SendText(csprintf("You add %s to your dwelling's access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
houseData->_accessList.push_back(targetId);
houseData->Save();
}
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseRemovePersonFromAccessList(std::string name)
{
uint32_t targetId = g_pWorld->GetPlayerId(name.c_str(), true);
if (!target)
{
m_pPlayer->SendText("Can't find a player by that name.", LTT_DEFAULT); //todo: made up message.
return;
}
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
auto iter = std::find(houseData->_accessList.begin(), houseData->_accessList.end(), targetId);
if (iter != houseData->_accessList.end())
{
m_pPlayer->SendText(csprintf("You remove %s from your dwelling's access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
houseData->_accessList.erase(iter);
houseData->Save();
}
else
{
m_pPlayer->SendText(csprintf("%s is not in the access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
}
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseToggleOpenAccess(bool newSetting)
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (houseData->_everyoneAccess != newSetting)
{
houseData->_everyoneAccess = newSetting;
if (newSetting)
m_pPlayer->SendText("Your dwelling is now open to the public.", LTT_DEFAULT); //todo: made up message.
else
m_pPlayer->SendText("Your dwelling is now private.", LTT_DEFAULT); //todo: made up message.
}
else
{
if (newSetting)
m_pPlayer->SendText("Your dwelling is already open to the public.", LTT_DEFAULT); //todo: made up message.
else
m_pPlayer->SendText("Your dwelling is already private.", LTT_DEFAULT); //todo: made up message.
}
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseToggleOpenStorageAccess()
{
//not sure how this worked? Is this a toggle? If not which command was used to disable it?
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (!houseData->_everyoneStorageAccess)
{
m_pPlayer->SendText("Your dwelling's storage is now open to the public.", LTT_DEFAULT); //todo: made up message.
houseData->_everyoneStorageAccess = true;
}
else
{
m_pPlayer->SendText("Your dwelling's storage is now private.", LTT_DEFAULT); //todo: made up message.
houseData->_everyoneStorageAccess = false;
}
houseData->Save();
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseAddOrRemovePersonToStorageList(std::string name, bool isAdd)
{
uint32_t targetId = g_pWorld->GetPlayerId(name.c_str(), true);
if (!target)
{
m_pPlayer->SendText("Can't find a player by that name.", LTT_DEFAULT); //todo: made up message.
return;
}
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (isAdd)
{
if (houseData->_accessList.size() > 128)
{
m_pPlayer->SendText("The storage access list is full", LTT_DEFAULT); //todo: made up message.
return;
}
if (std::find(houseData->_storageAccessList.begin(), houseData->_storageAccessList.end(), targetId) != houseData->_storageAccessList.end())
{
m_pPlayer->SendText(csprintf("%s is already in the storage access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
}
else
{
m_pPlayer->SendText(csprintf("You add %s to your dwelling's storage access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
houseData->_storageAccessList.push_back(targetId);
houseData->Save();
}
}
else
{
auto iter = std::find(houseData->_storageAccessList.begin(), houseData->_storageAccessList.end(), targetId);
if (iter != houseData->_storageAccessList.end())
{
m_pPlayer->SendText(csprintf("You remove %s from your dwelling's storage access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
houseData->_storageAccessList.erase(iter);
houseData->Save();
}
else
{
m_pPlayer->SendText(csprintf("%s is not in the storage access list", name.c_str()), LTT_DEFAULT); //todo: made up message.
}
}
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::GetHousesAvailable(uint32_t houseType)
{
std::tuple<int, std::list<uint32_t>> houses = g_pHouseManager->GetAvailableHouses(static_cast<HouseType>(houseType));
BinaryWriter msg;
msg.Write<uint32_t>(0x271);
msg.Write<uint32_t>(houseType);
msg.Write<uint32_t>(std::get<1>(houses).size());
for (auto ah : std::get<1>(houses))
{
msg.Write<uint32_t>(ah);
}
msg.Write<uint32_t>(std::get<0>(houses));
m_pPlayer->SendNetMessage(&msg, PRIVATE_MSG, TRUE, FALSE);
}
void CClientEvents::HouseAddOrRemoveAllegianceToAccessList(bool isAdd)
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (houseData->_allegianceAccess != isAdd)
{
houseData->_allegianceAccess = isAdd;
if (isAdd)
m_pPlayer->SendText("You have granted your monarchy access to your dwelling.", LTT_DEFAULT);
else
m_pPlayer->SendText("You have revoked access to your dwelling to your monarchy.", LTT_DEFAULT);
}
else
{
if (isAdd)
m_pPlayer->SendText("The monarchy already has access to your dwelling.", LTT_DEFAULT);
else
m_pPlayer->SendText("The monarchy did not have access to your dwelling.", LTT_DEFAULT);
}
houseData->Save();
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseAddOrRemoveAllegianceToStorageList(bool isAdd)
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
if (houseData->_allegianceStorageAccess != isAdd)
{
houseData->_allegianceStorageAccess = isAdd;
if (isAdd)
m_pPlayer->SendText("You have granted your monarchy access to your storage.", LTT_DEFAULT);
else
m_pPlayer->SendText("You have revoked storage access to your monarchy.", LTT_DEFAULT);
}
else
{
if (isAdd)
m_pPlayer->SendText("The monarchy already has storage access in your dwelling.", LTT_DEFAULT);
else
m_pPlayer->SendText("The monarchy did not have storage access in your dwelling.", LTT_DEFAULT);
}
houseData->Save();
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseClearAccessList()
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
houseData->_everyoneAccess = false;
houseData->_allegianceAccess = false;
if (houseData->_accessList.empty())
{
houseData->_accessList.clear();
houseData->Save();
m_pPlayer->SendText("Your clear the dwelling's access list.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("There's no one in the dwelling's access list.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::HouseClearStorageAccess()
{
uint32_t houseId = m_pPlayer->InqDIDQuality(HOUSEID_DID, 0);
if (houseId)
{
CHouseData *houseData = g_pHouseManager->GetHouseData(houseId);
if (houseData->_ownerId == m_pPlayer->GetID())
{
houseData->_everyoneStorageAccess = false;
houseData->_allegianceStorageAccess = false;
if (houseData->_storageAccessList.empty())
{
houseData->_storageAccessList.clear();
houseData->Save();
m_pPlayer->SendText("Your clear the dwelling's storage list.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("There's no one in the dwelling's storage list.", LTT_DEFAULT); //todo: made up message.
}
else
m_pPlayer->SendText("Only the character who owns the house may use this command.", LTT_DEFAULT);
}
else
m_pPlayer->SendText("You do not own a house.", LTT_DEFAULT);
}
void CClientEvents::NoLongerViewingContents(uint32_t container_id)
{
if (CWeenieObject *remoteContainerObj = g_pWorld->FindObject(container_id))
{
if (CContainerWeenie *remoteContainer = remoteContainerObj->AsContainer())
{
remoteContainer->HandleNoLongerViewing(m_pPlayer);
}
}
}
void CClientEvents::ChangePlayerOption(PlayerOptions option, bool value)
{
auto changeCharOption = [&](uint32_t optionBit)
{
m_pPlayer->_playerModule.options_ &= ~optionBit;
if (value)
m_pPlayer->_playerModule.options_ |= optionBit;
};
auto changeCharOption2 = [&](uint32_t optionBit)
{
m_pPlayer->_playerModule.options2_ &= ~optionBit;
if (value)
m_pPlayer->_playerModule.options2_ |= optionBit;
};
switch (option)
{
case AutoRepeatAttack_PlayerOption:
changeCharOption(AutoRepeatAttack_CharacterOption);
break;
case IgnoreAllegianceRequests_PlayerOption:
changeCharOption(IgnoreAllegianceRequests_CharacterOption);
break;
case IgnoreFellowshipRequests_PlayerOption:
changeCharOption(IgnoreFellowshipRequests_CharacterOption);
break;
case IgnoreTradeRequests_PlayerOption:
changeCharOption(IgnoreAllegianceRequests_CharacterOption);
break;
case DisableMostWeatherEffects_PlayerOption:
changeCharOption(DisableMostWeatherEffects_CharacterOption);
break;
case PersistentAtDay_PlayerOption:
changeCharOption2(PersistentAtDay_CharacterOptions2);
break;
case AllowGive_PlayerOption:
changeCharOption(AllowGive_CharacterOption);
break;
case ViewCombatTarget_PlayerOption:
changeCharOption(ViewCombatTarget_CharacterOption);
break;
case ShowTooltips_PlayerOption:
changeCharOption(ShowTooltips_CharacterOption);
break;
case UseDeception_PlayerOption:
changeCharOption(UseDeception_CharacterOption);
break;
case ToggleRun_PlayerOption:
changeCharOption(ToggleRun_CharacterOption);
break;
case StayInChatMode_PlayerOption:
changeCharOption(StayInChatMode_CharacterOption);
break;
case AdvancedCombatUI_PlayerOption:
changeCharOption(AdvancedCombatUI_CharacterOption);
break;
case AutoTarget_PlayerOption:
changeCharOption(AutoTarget_CharacterOption);
break;
case VividTargetingIndicator_PlayerOption:
changeCharOption(VividTargetingIndicator_CharacterOption);
break;
case FellowshipShareXP_PlayerOption:
changeCharOption(FellowshipShareXP_CharacterOption);
break;
case AcceptLootPermits_PlayerOption:
changeCharOption(AcceptLootPermits_PlayerOption);
break;
case FellowshipShareLoot_PlayerOption:
changeCharOption(FellowshipShareLoot_CharacterOption);
break;
case FellowshipAutoAcceptRequests_PlayerOption:
changeCharOption(FellowshipAutoAcceptRequests_PlayerOption);
break;
case SideBySideVitals_PlayerOption:
changeCharOption(SideBySideVitals_CharacterOption);
break;
case CoordinatesOnRadar_PlayerOption:
changeCharOption(CoordinatesOnRadar_CharacterOption);
break;
case SpellDuration_PlayerOption:
changeCharOption(SpellDuration_CharacterOption);
break;
case DisableHouseRestrictionEffects_PlayerOption:
changeCharOption(DisableHouseRestrictionEffects_PlayerOption);
break;
case DragItemOnPlayerOpensSecureTrade_PlayerOption:
changeCharOption(DragItemOnPlayerOpensSecureTrade_CharacterOption);
break;
case DisplayAllegianceLogonNotifications_PlayerOption:
changeCharOption(DisplayAllegianceLogonNotifications_CharacterOption);
break;
case UseChargeAttack_PlayerOption:
changeCharOption(UseChargeAttack_CharacterOption);
break;
case UseCraftSuccessDialog_PlayerOption:
changeCharOption(UseCraftSuccessDialog_CharacterOption);
break;
case HearAllegianceChat_PlayerOption:
changeCharOption(HearAllegianceChat_CharacterOption);
if (value)
SendText("Joined Allegiance Channel!", LTT_CHANNEL_SEND);
else
SendText("Left Allegiance Channel!", LTT_CHANNEL_SEND);
break;
case DisplayDateOfBirth_PlayerOption:
changeCharOption2(DisplayDateOfBirth_CharacterOptions2);
break;
case DisplayAge_PlayerOption:
changeCharOption2(DisplayAge_CharacterOptions2);
break;
case DisplayChessRank_PlayerOption:
changeCharOption2(DisplayChessRank_CharacterOptions2);
break;
case DisplayFishingSkill_PlayerOption:
changeCharOption2(DisplayFishingSkill_CharacterOptions2);
break;
case DisplayNumberDeaths_PlayerOption:
changeCharOption2(DisplayNumberDeaths_CharacterOptions2);
break;
case DisplayTimeStamps_PlayerOption:
changeCharOption2(TimeStamp_CharacterOptions2);
break;
case SalvageMultiple_PlayerOption:
changeCharOption(SalvageMultiple_CharacterOptions2);
break;
case HearGeneralChat_PlayerOption:
changeCharOption2(HearGeneralChat_CharacterOptions2);
if (value)
SendText("Joined General Channel!", LTT_CHANNEL_SEND);
else
SendText("Left General Channel!", LTT_CHANNEL_SEND);
break;
case HearTradeChat_PlayerOption:
changeCharOption2(HearTradeChat_CharacterOptions2);
if (value)
SendText("Joined Trade Channel!", LTT_CHANNEL_SEND);
else
SendText("Left Trade Channel!", LTT_CHANNEL_SEND);
break;
case HearLFGChat_PlayerOption:
changeCharOption2(HearLFGChat_CharacterOptions2);
if (value)
SendText("Joined LFG Channel!", LTT_CHANNEL_SEND);
else
SendText("Left LFG Channel!", LTT_CHANNEL_SEND);
break;
case HearRoleplayChat_PlayerOption:
changeCharOption2(HearRoleplayChat_CharacterOptions2);
if (value)
SendText("Joined Roleplay Channel!", LTT_CHANNEL_SEND);
else
SendText("Left Roleplay Channel!", LTT_CHANNEL_SEND);
break;
case AppearOffline_PlayerOption:
changeCharOption2(AppearOffline_CharacterOptions2);
break;
case DisplayNumberCharacterTitles_PlayerOption:
changeCharOption2(DisplayNumberCharacterTitles_CharacterOptions2);
break;
case MainPackPreferred_PlayerOption:
changeCharOption2(MainPackPreferred_CharacterOptions2);
break;
case LeadMissileTargets_PlayerOption:
changeCharOption2(LeadMissileTargets_CharacterOptions2);
break;
case UseFastMissiles_PlayerOption:
changeCharOption2(UseFastMissiles_CharacterOptions2);
break;
case FilterLanguage_PlayerOption:
changeCharOption2(FilterLanguage_CharacterOptions2);
break;
case ConfirmVolatileRareUse_PlayerOption:
changeCharOption2(ConfirmVolatileRareUse_CharacterOptions2);
break;
case HearSocietyChat_PlayerOption:
changeCharOption2(HearSocietyChat_CharacterOptions2);
break;
case ShowHelm_PlayerOption:
changeCharOption2(ShowHelm_CharacterOptions2);
break;
case DisableDistanceFog_PlayerOption:
changeCharOption2(DisableDistanceFog_CharacterOptions2);
break;
case UseMouseTurning_PlayerOption:
changeCharOption2(UseMouseTurning_CharacterOptions2);
break;
case ShowCloak_PlayerOption:
changeCharOption2(ShowCloak_CharacterOptions2);
break;
case LockUI_PlayerOption:
changeCharOption2(LockUI_CharacterOptions2);
break;
case TotalNumberOfPlayerOptions_PlayerOption:
changeCharOption2(TotalNumberOfPlayerOptions_PlayerOption);
break;
}
}
bool CClientEvents::CheckForChatSpam()
{
if (_next_chat_allowed > Timer::cur_time)
{
return false;
}
if (_next_chat_interval < Timer::cur_time)
{
_next_chat_interval = Timer::cur_time + 8.0;
_num_chat_sent = 0;
}
_num_chat_sent++;
if (_num_chat_sent > 8)
{
_next_chat_allowed = Timer::cur_time + 10.0;
m_pPlayer->SendText("You are sending too many messages and have been temporarily muted.", LTT_DEFAULT);
return false;
}
return true;
}
void CClientEvents::SetCharacterSquelchSetting(bool squelchSet, uint32_t squelchPlayer, std::string squelchName, BYTE squelchChatType, bool account)
{
if (squelchPlayer == 0 && squelchName.size() == 0)
return;
CharacterSquelch_t squelchSettings;
if (squelchPlayer == 0)
{
uint32_t playerId = g_pWorld->GetPlayerId(squelchName.c_str(), true);
squelchSettings.squelched_id = playerId;
squelchSettings.account_id = g_pDBIO->GetPlayerAccountId(playerId);
}
else
{
squelchSettings.squelched_id = squelchPlayer;
squelchSettings.account_id = g_pDBIO->GetPlayerAccountId(squelchPlayer);
}
if (squelchSettings.squelched_id == 0)
return;
switch (squelchChatType)
{
case SquelchTypes::Speech_Squelch:
squelchSettings.isSpeech = squelchSet;
break;
case 0x03:
squelchSettings.isTell = squelchSet;
break;
case 0x06:
squelchSettings.isCombat = squelchSet;
break;
case 0x07:
squelchSettings.isMagic = squelchSet;
break;
case 0x0C:
squelchSettings.isEmote = squelchSet;
break;
case 0x0D:
squelchSettings.isAdvancement = squelchSet;
break;
case 0x10:
squelchSettings.isAppraisal = squelchSet;
break;
case 0x11:
squelchSettings.isSpellcasting = squelchSet;
break;
case 0x12:
squelchSettings.isAllegiance = squelchSet;
break;
case 0x13:
squelchSettings.isFellowship = squelchSet;
break;
case 0x15:
squelchSettings.isCombatEnemy = squelchSet;
break;
case 0x17:
squelchSettings.isRecall = squelchSet;
break;
case 0x18:
squelchSettings.isCrafting = squelchSet;
break;
default:
break;
}
if (squelchChatType == SquelchTypes::AllChannels_Squelch)
{
squelchSettings.isSpeech = true;
squelchSettings.isTell = true;
squelchSettings.isCombat = true;
squelchSettings.isMagic = true;
squelchSettings.isEmote = true;
squelchSettings.isAdvancement = true;
squelchSettings.isAppraisal = true;
squelchSettings.isSpellcasting = true;
squelchSettings.isAllegiance = true;
squelchSettings.isFellowship = true;
squelchSettings.isCombatEnemy = true;
squelchSettings.isRecall = true;
squelchSettings.isCrafting = true;
}
if (squelchSet)
{
if (!g_pDBIO->SaveCharacterSquelch(m_pPlayer->GetID(), squelchSettings))
{
DEBUG_DATA << "Failed to save squelch data";
}
}
else
{
if (!g_pDBIO->RemoveCharacterSquelch(m_pPlayer->GetID(), squelchSettings))
{
DEBUG_DATA << "Failed to remove squelch data";
}
}
m_pPlayer->LoadSquelches();
SendSquelchDB();
}
void CClientEvents::PKLiteEnable()
{
double dt = 0;
int it = 0;
if (m_pPlayer->m_Qualities.InqFloat(PK_TIMESTAMP_FLOAT, dt) ||
(m_pPlayer->m_Qualities.InqInt(PLAYER_KILLER_STATUS_INT, it) && (it & PKStatusEnum::PK_PKStatus)))
{
m_pPlayer->NotifyWeenieError(WERROR_PK_SWITCH_PKLITE_ON_PK);
return;
}
if (m_pPlayer->IsBusyOrInAction())
{
m_pPlayer->NotifyWeenieError(WERROR_ACTIONS_LOCKED);
return;
}
CPlayerWeenie *player = m_pPlayer;
motion_use_event *pkl = new motion_use_event(Motion_EnterPKLite, [player](uint32_t motion)
{
player->m_Qualities.SetInt(PLAYER_KILLER_STATUS_INT, PKStatusEnum::PKLite_PKStatus);
player->NotifyIntStatUpdated(PLAYER_KILLER_STATUS_INT, false);
player->SendText(csprintf("%s is looking for a fight!", player->GetName().c_str()), LTT_GENERAL_CHANNEL);
player->NotifyWeenieError(WERROR_PK_SWITCH_PKLITE_ON);
});
player->ExecuteUseEvent(pkl);
}
// This is it!
void CClientEvents::ProcessEvent(BinaryReader *pReader)
{
if (!m_pPlayer)
return;
if (m_pPlayer->IsPkLogging())
return;
uint32_t dwSequence = pReader->ReadUInt32();
uint32_t dwEvent = pReader->ReadUInt32();
if (pReader->GetLastError()) return;
#ifdef _DEBUG
string enumString = "";
switch (dwEvent)
{
case 0x0005: enumString = "CHANGE_PLAYER_OPTION "; break;
case 0x0008: enumString = "MELEE_ATTACK "; break;
case 0x000A: enumString = "MISSILE_ATTACK "; break;
case 0x000F: enumString = "SET_AFK_MODE "; break;
case 0x0010: enumString = "SET_AFK_MESSAGE "; break;
case 0x0015: enumString = "TEXT_CLIENT "; break;
case 0x0017: enumString = "REMOVE_FRIEND "; break;
case 0x0018: enumString = "ADD_FRIEND "; break;
case 0x0019: enumString = "STORE_ITEM "; break;
case 0x001A: enumString = "EQUIP_ITEM "; break;
case 0x001B: enumString = "DROP_ITEM "; break;
case 0x001D: enumString = "ALLEGIANCE_SWEAR "; break;
case 0x001E: enumString = "ALLEGIANCE_BREAK "; break;
case 0x001F: enumString = "ALLEGIANCE_SEND_UPDATES "; break;
case 0x0025: enumString = "CLEAR_FRIENDS "; break;
case 0x0026: enumString = "RECALL_PKL_ARENA "; break;
case 0x0027: enumString = "RECALL_PK_ARENA "; break;
case 0x002C: enumString = "SET_DISPLAY_TITLE "; break;
case 0x0275: enumString = "CONFIRMATION_RESPONSE "; break;
case 0x027D: enumString = "UST_SALVAGE_REQUEST "; break;
case 0x0030: enumString = "ALLEGIANCE_QUERY_NAME "; break;
case 0x0031: enumString = "ALLEGIANCE_CLEAR_NAME "; break;
case 0x0032: enumString = "SEND_TELL_BY_GUID "; break;
case 0x0033: enumString = "ALLEGIANCE_SET_NAME "; break;
case 0x0035: enumString = "USE_ITEM_EX "; break;
case 0x0036: enumString = "USE_OBJECT "; break;
case 0x003B: enumString = "ALLEGIANCE_SET_OFFICER "; break;
case 0x003C: enumString = "ALLEGIANCE_SET_OFFICER_TITLE "; break;
case 0x003D: enumString = "ALLEGIANCE_LIST_OFFICER_TITLES "; break;
case 0x003E: enumString = "ALLEGIANCE_CLEAR_OFFICER_TITLES "; break;
case 0x003F: enumString = "ALLEGIANCE_LOCK_ACTION "; break;
case 0x0040: enumString = "ALLEGIANCE_APPROVED_VASSAL "; break;
case 0x0041: enumString = "ALLEGIANCE_CHAT_GAG "; break;
case 0x0042: enumString = "ALLEGIANCE_HOUSE_ACTION "; break;
case 0x0044: enumString = "SPEND_XP_VITALS "; break;
case 0x0045: enumString = "SPEND_XP_ATTRIBUTES "; break;
case 0x0046: enumString = "SPEND_XP_SKILLS "; break;
case 0x0047: enumString = "SPEND_SKILL_CREDITS "; break;
case 0x0048: enumString = "CAST_UNTARGETED_SPELL "; break;
case 0x004A: enumString = "CAST_TARGETED_SPELL "; break;
case 0x0053: enumString = "CHANGE_COMBAT_STANCE "; break;
case 0x0054: enumString = "STACKABLE_MERGE "; break;
case 0x0055: enumString = "STACKABLE_SPLIT_TO_CONTAINER "; break;
case 0x0056: enumString = "STACKABLE_SPLIT_TO_3D "; break;
case 0x019B: enumString = "STACKABLE_SPLIT_TO_WIELD "; break;
case 0x0058: enumString = "SQUELCH_CHARACTER_MODIFY "; break;
case 0x0059: enumString = "SQUELCH_ACCOUNT_MODIFY "; break;
case 0x005B: enumString = "SQUELCH_GLOBAL_MODIFY "; break;
case 0x005D: enumString = "SEND_TELL_BY_NAME "; break;
case 0x005F: enumString = "BUY_FROM_VENDOR "; break;
case 0x0060: enumString = "SELL_TO_VENDOR "; break;
case 0x0063: enumString = "RECALL_LIFESTONE "; break;
case 0x00A1: enumString = "LOGIN_COMPLETE "; break;
case 0x00A2: enumString = "FELLOW_CREATE "; break;
case 0x00A3: enumString = "FELLOW_QUIT "; break;
case 0x00A4: enumString = "FELLOW_DISMISS "; break;
case 0x00A5: enumString = "FELLOW_RECRUIT "; break;
case 0x00A6: enumString = "FELLOW_UPDATE "; break;
case 0x00AA: enumString = "BOOK_ADD_PAGE "; break;
case 0x00AB: enumString = "BOOK_MODIFY_PAGE "; break;
case 0x00AC: enumString = "BOOK_DATA "; break;
case 0x00AD: enumString = "BOOK_DELETE_PAGE "; break;
case 0x00AE: enumString = "BOOK_PAGE_DATA "; break;
case 0x00CD: enumString = "GIVE_OBJECT "; break;
case 0x00BF: enumString = "INSCRIBE "; break;
case 0x00C8: enumString = "APPRAISE "; break;
case 0x00D6: enumString = "ADMIN_TELEPORT "; break;
case 0x0140: enumString = "ABUSE_LOG_REQUEST "; break;
case 0x0145: enumString = "CHANNEL_ADD "; break;
case 0x0146: enumString = "CHANNEL_REMOVE "; break;
case 0x0147: enumString = "CHANNEL_TEXT "; break;
case 0x0148: enumString = "CHANNEL_LIST "; break;
case 0x0149: enumString = "CHANNEL_INDEX "; break;
case 0x0195: enumString = "NO_LONGER_VIEWING_CONTAINER "; break;
case 0x019C: enumString = "ADD_ITEM_SHORTCUT "; break;
case 0x019D: enumString = "REMOVE_ITEM_SHORTCUT "; break;
case 0x01A1: enumString = "CHARACTER_OPTIONS "; break;
case 0x01A8: enumString = "SPELLBOOK_REMOVE "; break;
case 0x01B7: enumString = "CANCEL_ATTACK "; break;
case 0x01BF: enumString = "QUERY_HEALTH "; break;
case 0x01C2: enumString = "QUERY_AGE "; break;
case 0x01C4: enumString = "QUERY_BIRTH "; break;
case 0x01DF: enumString = "TEXT_INDIRECT "; break;
case 0x01E1: enumString = "TEXT_EMOTE "; break;
case 0x01E3: enumString = "ADD_TO_SPELLBAR "; break;
case 0x01E4: enumString = "REMOVE_FROM_SPELLBAR "; break;
case 0x01E9: enumString = "PING "; break;
case 0x1F6: enumString = "TRADE_OPEN "; break;
case 0x1F7: enumString = "TRADE_CLOSE "; break;
case 0x1F8: enumString = "TRADE_ADD "; break;
case 0x1FA: enumString = "TRADE_ACCEPT "; break;
case 0x1FB: enumString = "TRADE_DECLINE "; break;
case 0x204: enumString = "TRADE_RESET "; break;
case 0x0216: enumString = "CLEAR_PLAYER_CONSENT_LIST "; break;
case 0x0217: enumString = "DISPLAY_PLAYER_CONSENT_LIST "; break;
case 0x0218: enumString = "REMOVE_FROM_PLAYER_CONSENT_LIST "; break;
case 0x0219: enumString = "ADD_PLAYER_PERMISSION "; break;
case 0x021A: enumString = "REMOVE_PLAYER_PERMISSION "; break;
case 0x021C: enumString = "HOUSE_BUY "; break;
case 0x021F: enumString = "HOUSE_ABANDON "; break;
case 0x21E: enumString = "HOUSE_OF_PLAYER_QUERY "; break;
case 0x0221: enumString = "HOUSE_RENT "; break;
case 0x0224: enumString = "DESIRED_COMPS_SET "; break;
case 0x0245: enumString = "HOUSE_ADD_GUEST "; break;
case 0x0246: enumString = "HOUSE_REMOVE_GUEST "; break;
case 0x0247: enumString = "HOUSE_SET_OPEN_ACCESS "; break;
case 0x0249: enumString = "HOUSE_CHANGE_STORAGE_PERMISSIONS "; break;
case 0x024A: enumString = "HOUSE_BOOT_GUEST "; break;
case 0x024C: enumString = "HOUSE_CLEAR_STORAGE_PERMISSIONS "; break;
case 0x024D: enumString = "HOUSE_GUEST_LIST "; break;
case 0x0254: enumString = "ALLEGIANCE_SET_MOTD "; break;
case 0x0255: enumString = "ALLEGIANCE_QUERY_MOTD "; break;
case 0x0256: enumString = "ALLEGIANCE_CLEAR_MOTD "; break;
case 0x0258: enumString = "HOUSE_QUERY_SLUMLORD "; break;
case 0x025C: enumString = "HOUSE_SET_OPEN_STORAGE_ACCESS "; break;
case 0x025E: enumString = "HOUSE_REMOVE_ALL_GUESTS "; break;
case 0x025F: enumString = "HOUSE_BOOT_ALL "; break;
case 0x0262: enumString = "RECALL_HOUSE "; break;
case 0x0263: enumString = "ITEM_MANA_REQUEST "; break;
case 0x0266: enumString = "HOUSE_SET_HOOKS_VISIBILITY "; break;
case 0x0267: enumString = "HOUSE_CHANGE_ALLEGIANCE_GUEST_PERMISSIONS "; break;
case 0x0268: enumString = "HOUSE_CHANGE_ALLEGIANCE_STORAGE_PERMISSIONS "; break;
case 0x0269: enumString = "CHESS_JOIN "; break;
case 0x026A: enumString = "CHESS_QUIT "; break;
case 0x026B: enumString = "CHESS_MOVE "; break;
case 0x026D: enumString = "CHESS_PASS "; break;
case 0x026E: enumString = "CHESS_STALEMATE "; break;
case 0x0270: enumString = "HOUSE_LIST_AVAILABLE "; break;
case 0x0277: enumString = "ALLEGIANCE_BOOT_PLAYER "; break;
case 0x0278: enumString = "RECALL_HOUSE_MANSION "; break;
case 0x0279: enumString = "DIE_COMMAND "; break;
case 0x027B: enumString = "ALLEGIANCE_INFO_REQUEST "; break;
case 0x0286: enumString = "SPELLBOOK_FILTERS "; break;
case 0x028D: enumString = "RECALL_MARKET "; break;
case 0x028F: enumString = "PKLITE "; break;
case 0x0290: enumString = "FELLOW_ASSIGN_NEW_LEADER "; break;
case 0x0291: enumString = "FELLOW_CHANGE_OPENNESS "; break;
case 0x02A0: enumString = "ALLEGIANCE_CHAT_BOOT "; break;
case 0x02A1: enumString = "ALLEGIANCE_ADD_PLAYER_BAN "; break;
case 0x02A2: enumString = "ALLEGIANCE_REMOVE_PLAYER_BAN "; break;
case 0x02A3: enumString = "ALLEGIANCE_LIST_BANS "; break;
case 0x02A5: enumString = "ALLEGIANCE_REMOVE_OFFICER "; break;
case 0x02A6: enumString = "ALLEGIANCE_LIST_OFFICERS "; break;
case 0x02A7: enumString = "ALLEGIANCE_CLEAR_OFFICERS "; break;
case 0x02AB: enumString = "RECALL_ALLEGIANCE_HOMETOWN "; break;
case 0x0311: enumString = "FINISH_BARBER "; break;
case 0x0316: enumString = "CONTRACT_ABANDON "; break;
case 0xF61B: enumString = "MOVEMENT_JUMP "; break;
case 0xF61C: enumString = "MOVEMENT_MOVE_TO_STATE "; break;
case 0xF61E: enumString = "MOVEMENT_DO_MOVEMENT_COMMAND "; break;
case 0xF649: enumString = "MOVEMENT_TURN_TO "; break;
case 0xF661: enumString = "MOVEMENT_STOP "; break;
case 0xF752: enumString = "MOVEMENT_AUTONOMY_LEVEL "; break;
case 0xF753: enumString = "MOVEMENT_AUTONOMOUS_POSITION "; break;
case 0xF7C9: enumString = "MOVEMENT_JUMP_NON_AUTONOMOUS "; break;
default:
DEBUG_DATA << "Processing Unknown event:" << dwEvent;
break;
};
DEBUG_DATA << "Processing event:" << enumString;
#endif
switch (dwEvent)
{
case CHANGE_PLAYER_OPTION: // Change player option
{
uint32_t option = pReader->ReadUInt32();
uint32_t value = pReader->ReadUInt32();
if (pReader->GetLastError())
break;
ChangePlayerOption((PlayerOptions)option, value ? true : false);
break;
}
case MELEE_ATTACK: // Melee Attack
{
uint32_t dwTarget = pReader->ReadUInt32();
uint32_t dwHeight = pReader->ReadUInt32();
float flPower = pReader->ReadFloat();
if (pReader->GetLastError()) break;
Attack(dwTarget, dwHeight, flPower);
break;
}
case MISSILE_ATTACK: // Missile Attack
{
uint32_t target = pReader->ReadUInt32();
uint32_t height = pReader->ReadUInt32();
float power = pReader->ReadFloat();
if (pReader->GetLastError()) break;
MissileAttack(target, height, power);
break;
}
case SET_AFK_MODE:
{
// Read: bool afk - Whether user is afk
break;
}
case SET_AFK_MESSAGE:
{
// Read: string message
break;
}
case TEXT_CLIENT: //Client Text
{
char *szText = pReader->ReadString();
if (pReader->GetLastError()) break;
ClientText(szText);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case REMOVE_FRIEND:
{
uint32_t dwFriend = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->RemoveFriend(dwFriend);
break;
}
case ADD_FRIEND:
{
char *szFriend = pReader->ReadString();
if (pReader->GetLastError()) break;
m_pPlayer->AddFriend(std::string(szFriend));
break;
}
case STORE_ITEM: //Store Item
{
uint32_t dwItemID = pReader->ReadUInt32();
uint32_t dwContainer = pReader->ReadUInt32();
uint32_t dwSlot = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->MoveItemToContainer(dwItemID, dwContainer, (char)dwSlot);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case EQUIP_ITEM: //Equip Item
{
uint32_t dwItemID = pReader->ReadUInt32();
uint32_t dwCoverage = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->MoveItemToWield(dwItemID, dwCoverage);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case DROP_ITEM: //Drop Item
{
uint32_t dwItemID = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->MoveItemTo3D(dwItemID);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case ALLEGIANCE_SWEAR: // Swear Allegiance request
{
MAllegianceSwear_001D msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_BREAK: // Break Allegiance request
{
MAllegianceBreak_001E msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_SEND_UPDATES: // Allegiance Update request
{
MAllegianceUpdate_001F msg(m_pClient);
msg.Parse(pReader);
break;
}
case CLEAR_FRIENDS:
{
// Nothing to read
break;
}
case RECALL_PKL_ARENA:
{
PKLArenaRecall();
break;
}
case RECALL_PK_ARENA:
{
PKArenaRecall();
break;
}
case SET_DISPLAY_TITLE:
{
uint32_t dwTitleID = pReader->ReadUInt32();
m_pPlayer->SetTitle(dwTitleID);
break;
}
case CONFIRMATION_RESPONSE: // confirmation response
{
// ConfirmationTypes: SwearAllegiance 1, AlterSkill 2, AlterAttribute 3, Fellowship 4, Craft 5, Augmentation 6, YesNo 7
uint32_t confirmType = pReader->ReadUInt32();
int context = pReader->ReadInt32();
bool accepted = pReader->ReadInt32();
switch (confirmType)
{
case 0x05: // crafting
if (accepted)
{
m_pPlayer->UseEx(true);
}
break;
case 0x06: // Augmentation
if (accepted)
{
m_pPlayer->UseEx(true);
}
break;
case 0x07:
CWeenieObject *target = g_pWorld->FindObject(context);
if (target)
{
target->MakeEmoteManager()->ConfirmationResponse(accepted, m_pPlayer->id);
}
}
break;
}
case UST_SALVAGE_REQUEST: // ust salvage request
{
uint32_t toolId = pReader->ReadUInt32();
PackableList<uint32_t> items;
items.UnPack(pReader);
if (pReader->GetLastError())
break;
if (items.size() <= 300) //just some sanity checking: 102 items in main pack + (24 * 7) items in subpacks = 270 items. 300 just to be safe.
m_pPlayer->PerformSalvaging(toolId, items);
break;
}
case ALLEGIANCE_QUERY_NAME:
{
MAllegianceNameQuery_0030 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_CLEAR_NAME:
{
MAllegianceNameClear_0031 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_SET_NAME:
{
MAllegianceNameSet_0033 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case USE_ITEM_EX: //Use Item Ex
{
uint32_t dwSourceID = pReader->ReadUInt32();
uint32_t dwDestID = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
UseItemEx(dwSourceID, dwDestID);
break;
}
case USE_OBJECT: //Use Object
{
uint32_t dwEID = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
UseObject(dwEID);
break;
}
case ALLEGIANCE_SET_OFFICER:
{
MAllegianceOfficerSet_003B msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_SET_OFFICER_TITLE:
{
MAllegianceOfficerTitleSet_003C msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_LIST_OFFICER_TITLES:
{
MAllegianceOfficerTitlesList_003D msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_CLEAR_OFFICER_TITLES:
{
MAllegianceOfficerTitlesClear_003E msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_LOCK_ACTION:
{
// Read: AllegianceLockAction action - LockedOff 1, LockedOn 2, ToggleLocked 3, CheckLocked 4, DisplayBypass 5, ClearBypass 6
MAllegianceLockAction_003F msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_APPROVED_VASSAL:
{
MAllegianceApprovedVassalSet_0040 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_CHAT_GAG:
{
// Read: string charName, bool on - player being gagged, whether gag is on
break;
}
case ALLEGIANCE_HOUSE_ACTION:
{
// Read: AllegianceHouseAction action - Help 1, GuestOpen 2, GuestClosed 3, StorageOpen 4, StorageClosed 5
break;
}
case SPEND_XP_VITALS: // spend XP on vitals
{
uint32_t dwAttribute2nd = pReader->ReadUInt32();
uint32_t dwXP = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
SpendAttribute2ndXP((STypeAttribute2nd)dwAttribute2nd, dwXP);
break;
}
case SPEND_XP_ATTRIBUTES: // spend XP on attributes
{
uint32_t dwAttribute = pReader->ReadUInt32();
uint32_t dwXP = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
SpendAttributeXP((STypeAttribute)dwAttribute, dwXP);
break;
}
case SPEND_XP_SKILLS: // spend XP on skills
{
uint32_t dwSkill = pReader->ReadUInt32();
uint32_t dwXP = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
SpendSkillXP((STypeSkill)dwSkill, dwXP);
break;
}
case SPEND_SKILL_CREDITS: // spend credits to train skill
{
uint32_t dwSkill = pReader->ReadUInt32();
uint32_t dwCredits = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
SpendSkillCredits((STypeSkill)dwSkill, dwCredits);
break;
}
case CAST_UNTARGETED_SPELL: // cast untargeted spell
{
uint32_t spell_id = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->TryCastSpell(m_pPlayer->GetID() /*0*/, spell_id);
break;
}
case CAST_TARGETED_SPELL: // cast targeted spell
{
uint32_t target = pReader->ReadUInt32();
uint32_t spell_id = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->TryCastSpell(target, spell_id);
break;
}
case CHANGE_COMBAT_STANCE: // Evt_Combat__ChangeCombatMode_ID "Change Combat Mode"
{
uint32_t mode = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
m_pPlayer->m_bChangingStance = true;
ChangeCombatStance((COMBAT_MODE)mode);
break;
}
case STACKABLE_MERGE: // Evt_Inventory__StackableMerge
{
uint32_t merge_from_id = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
uint32_t merge_to_id = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
uint32_t amount = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
if (!g_pWorld->IsItemInUse(merge_from_id))
{
m_pPlayer->MergeItem(merge_from_id, merge_to_id, amount);
g_pWorld->RemoveMergedItem(merge_from_id);
}
else
{
m_pPlayer->NotifyInventoryFailedEvent(merge_from_id, WERROR_OBJECT_GONE);
}
break;
}
case STACKABLE_SPLIT_TO_CONTAINER: // Evt_Inventory__StackableSplitToContainer
{
uint32_t stack_id = pReader->Read<uint32_t>();
uint32_t container_id = pReader->Read<uint32_t>();
uint32_t place = pReader->Read<uint32_t>();
uint32_t amount = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
m_pPlayer->SplitItemToContainer(stack_id, container_id, place, amount);
break;
}
case STACKABLE_SPLIT_TO_3D: // Evt_Inventory__StackableSplitTo3D
{
uint32_t stack_id = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
uint32_t amount = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
m_pPlayer->SplitItemto3D(stack_id, amount);
break;
}
case STACKABLE_SPLIT_TO_WIELD: // Evt_Inventory__StackableSplitToWield
{
uint32_t stack_id = pReader->Read<uint32_t>();
uint32_t loc = pReader->Read<uint32_t>();
uint32_t amount = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
m_pPlayer->SplitItemToWield(stack_id, loc, amount);
break;
}
case SQUELCH_CHARACTER_MODIFY:
{
// Read: bool add, uint32_t characterID, string characterName, ChatMessageType msgType
// 0 = unsquelch, 1 = squelch, ChatMessageType = enum LogTextType
bool squelchSet = pReader->Read<uint32_t>() > 0;
if (pReader->GetLastError()) break;
uint32_t squelchPlayer = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
std::string squelchName = pReader->ReadString();
if (pReader->GetLastError()) break;
BYTE squelchChatType = pReader->ReadByte();
if (pReader->GetLastError()) break;
CPlayerWeenie* target = g_pWorld->FindPlayer(squelchName.c_str());
if (!target)
{
m_pPlayer->SendText("Player not found.", LTT_DEFAULT);
break;
}
SetCharacterSquelchSetting(squelchSet, squelchPlayer, squelchName, squelchChatType, TRUE);
break;
}
case SQUELCH_ACCOUNT_MODIFY:
{
// Read: bool add, string characterName
bool squelchSet = pReader->Read<uint32_t>() > 0;
if (pReader->GetLastError()) break;
std::string squelchName = pReader->ReadString();
if (pReader->GetLastError()) break;
SetCharacterSquelchSetting(squelchSet, 0, squelchName, 0, TRUE);
break;
}
case SQUELCH_GLOBAL_MODIFY:
{
// Read: bool add, ChatMessageType msgType
break;
}
case SEND_TELL_BY_NAME: //Send Tell by Name
{
char* szText = pReader->ReadString();
if (pReader->GetLastError()) break;
char* szName = pReader->ReadString();
if (pReader->GetLastError()) break;
SendTell(szText, szName, 0);
break;
}
case SEND_TELL_BY_GUID: //Send Tell by GUID
{
char *text = pReader->ReadString();
if (pReader->GetLastError()) break;
uint32_t GUID = pReader->ReadUInt32();
if (pReader->GetLastError()) break;
if (GUID > 0)
SendTell(text, NULL, GUID);
else
m_pPlayer->SendText("Player not found.", LTT_DEFAULT);
break;
}
case BUY_FROM_VENDOR: // Buy from Vendor
{
uint32_t vendorID = pReader->Read<uint32_t>();
uint32_t numItems = pReader->Read<uint32_t>();
if (numItems >= 300)
break;
bool error = false;
std::list<ItemProfile *> items;
for (uint32_t i = 0; i < numItems; i++)
{
ItemProfile *item = new ItemProfile();
error = item->UnPack(pReader);
items.push_back(item);
if (error || pReader->GetLastError())
break;
}
if (!error && !pReader->GetLastError())
{
TryBuyItems(vendorID, items);
}
for (auto item : items)
{
delete item;
}
break;
}
case SELL_TO_VENDOR: // Sell to Vendor
{
uint32_t vendorID = pReader->Read<uint32_t>();
uint32_t numItems = pReader->Read<uint32_t>();
if (numItems >= 300)
break;
bool error = false;
std::list<ItemProfile *> items;
for (uint32_t i = 0; i < numItems; i++)
{
std::unique_ptr<ItemProfile> item(new ItemProfile());
error = item->UnPack(pReader);
if (error || pReader->GetLastError())
break;
else
{
CWeenieObject* itemToSell = m_pPlayer->FindContainedItem(item->iid);
if (itemToSell && itemToSell->IsSellable() && !itemToSell->IsRetained())
{
items.push_back(item.release());
}
}
}
if (!error && !pReader->GetLastError())
{
TrySellItems(vendorID, items);
}
for (auto item : items)
{
delete item;
}
break;
}
case RECALL_LIFESTONE: // Lifestone Recall
{
LifestoneRecall();
break;
}
case LOGIN_COMPLETE: // "Login Complete"
{
ExitPortal();
break;
}
case FELLOW_CREATE: // "Create Fellowship"
{
std::string name = pReader->ReadString();
int shareXP = pReader->Read<int>();
if (pReader->GetLastError())
break;
TryFellowshipCreate(name, shareXP);
break;
}
case FELLOW_QUIT: // "Quit Fellowship"
{
int disband = pReader->Read<int>();
if (pReader->GetLastError())
break;
TryFellowshipQuit(disband);
break;
}
case FELLOW_DISMISS: // "Fellowship Dismiss"
{
uint32_t dismissed = pReader->Read<uint32_t>();
if (pReader->GetLastError()) break;
TryFellowshipDismiss(dismissed);
break;
}
case FELLOW_RECRUIT: // "Fellowship Recruit"
{
uint32_t target = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
TryFellowshipRecruit(target);
break;
}
case FELLOW_UPDATE: // "Fellowship Update"
{
int on = pReader->Read<int>();
if (pReader->GetLastError())
break;
TryFellowshipUpdate(on);
break;
}
case BOOK_ADD_PAGE:
{
// Read: uint32_t objectID - ID of book
break;
}
case BOOK_MODIFY_PAGE:
{
// Read: uint32_t objectID, int pageNum, string pageText
break;
}
case BOOK_DATA: // Request update to book data (seems to be after failed add page)
{
// Read: uint32_t objectID - ID of book
break;
}
case BOOK_DELETE_PAGE:
{
// Read: uint32_t objectID, int pageNum
break;
}
case BOOK_PAGE_DATA: // Requests data of a page of a book
{
// Read: uint32_t objectID, int pageNum
break;
}
case GIVE_OBJECT: // Give an item to someone
{
uint32_t target_id = pReader->Read<uint32_t>();
uint32_t object_id = pReader->Read<uint32_t>();
uint32_t amount = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
m_pPlayer->GiveItem(target_id, object_id, amount);
break;
}
case INSCRIBE: // "Inscribe"
{
uint32_t target_id = pReader->Read<uint32_t>();
std::string msg = pReader->ReadString();
if (pReader->GetLastError())
break;
TryInscribeItem(target_id, msg);
break;
}
case APPRAISE: // Identify
{
uint32_t target_id = pReader->ReadUInt32();
if (pReader->GetLastError())
break;
Identify(target_id);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case ADMIN_TELEPORT: // Advocate teleport (triggered by having an admin flag set, clicking the mini-map)
{
if (m_pPlayer->GetAccessLevel() < ADVOCATE_ACCESS)
break;
// Starts with string (was empty when I tested)
pReader->ReadString();
// Then position (target)
Position position;
position.UnPack(pReader);
if (pReader->GetLastError())
break;
m_pPlayer->Movement_Teleport(position);
break;
}
case ABUSE_LOG_REQUEST: // Send abuse report
{
// Read: string target, uint status = 1, string complaint
break;
}
case CHANNEL_ADD: // Join chat channel
{
// Read: uint channel ID
break;
}
case CHANNEL_REMOVE: // Leave chat channel
{
// Read: uint channel ID
break;
}
case CHANNEL_TEXT: // Channel Text
{
uint32_t channel_id = pReader->ReadUInt32();
char *msg = pReader->ReadString();
if (pReader->GetLastError())
break;
if (CheckForChatSpam())
{
std::string filteredText = FilterBadChatCharacters(msg);
ChannelText(channel_id, filteredText.c_str());
}
break;
}
case CHANNEL_LIST: // List of characters listening to a channel
{
// Read: PackableList<string> characters
break;
}
case CHANNEL_INDEX: // List of channels available to the player
{
// Read: PackableList<string> channels
break;
}
case NO_LONGER_VIEWING_CONTAINER: // No longer viewing contents
{
uint32_t container_id = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
NoLongerViewingContents(container_id);
break;
}
case ADD_ITEM_SHORTCUT: // Add item to shortcut bar
{
ShortCutData data;
data.UnPack(pReader);
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.AddShortCut(data);
break;
}
case REMOVE_ITEM_SHORTCUT: // Add item to shortcut bar
{
int index = pReader->Read<int>();
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.RemoveShortCut(index);
break;
}
case CHARACTER_OPTIONS: // Character Options
{
PlayerModule module;
if (!module.UnPack(pReader) || pReader->GetLastError())
break;
if (m_pPlayer && m_pPlayer->AsPlayer())
{
SendText("Updating character configuration.", LTT_SYSTEM_EVENT);
m_pPlayer->UpdateModuleFromClient(module);
}
else
DEBUG_DATA << "Playermodule call for invalid object";
break;
}
case CANCEL_ATTACK: // Cancel attack
{
// TODO
m_pPlayer->TryCancelAttack();
break;
}
case SPELLBOOK_REMOVE:
{
uint32_t spell_id = pReader->ReadUInt32();
if (pReader->GetLastError())
break;
if (spell_id)
{
if (const CSpellBase *spell = MagicSystem::GetSpellTable()->GetSpellBase(spell_id))
{
if (m_pPlayer->m_Qualities._spell_book && m_pPlayer->m_Qualities._spell_book->Exists(spell_id))
{
m_pPlayer->m_Qualities.RemoveSpell(spell_id);
BinaryWriter removeSpellFromSpellbook;
removeSpellFromSpellbook.Write<uint32_t>(0x1A8);
removeSpellFromSpellbook.Write<uint32_t>(spell_id);
m_pPlayer->SendNetMessage(&removeSpellFromSpellbook, PRIVATE_MSG, TRUE, FALSE);
m_pPlayer->SendText(csprintf("You no longer know the %s spell.", spell->_name.c_str()), LTT_DEFAULT);
}
else
SendText("You don't know that spell!", LTT_DEFAULT);
}
}
break;
}
case QUERY_HEALTH: // Request health update
{
uint32_t target_id = pReader->ReadUInt32();
if (pReader->GetLastError())
break;
RequestHealthUpdate(target_id);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case QUERY_AGE:
{
uint32_t targetID = pReader->ReadUInt32(); // don't need this, query can't target anyone else
if (pReader->GetLastError())
break;
int age = m_pPlayer->m_Qualities.GetInt(AGE_INT, 0);
SendText(GetAgeString(age).c_str(), LTT_DEFAULT); // You have played for Xm Xd Xh Xm Xs.
break;
}
case QUERY_BIRTH:
{
uint32_t targetID = pReader->ReadUInt32(); // don't need this, query can't target anyone else
if (pReader->GetLastError())
break;
std::string DOB = m_pPlayer->InqStringQuality(DATE_OF_BIRTH_STRING, "");
SendText(csprintf("%s", DOB.c_str()), LTT_DEFAULT);
break;
}
case TEXT_INDIRECT: // Indirect Text (@me)
{
char *msg = pReader->ReadString();
if (pReader->GetLastError())
break;
if (CheckForChatSpam())
{
std::string filteredText = FilterBadChatCharacters(msg);
EmoteText(filteredText.c_str());
}
break;
}
case TEXT_EMOTE: // Emote Text (*laugh* sends 'laughs')
{
char *msg = pReader->ReadString();
if (pReader->GetLastError())
break;
if (CheckForChatSpam())
{
std::string filteredText = FilterBadChatCharacters(msg);
ActionText(filteredText.c_str());
}
break;
}
case ADD_TO_SPELLBAR: // Add item to spell bar
{
uint32_t spellID = pReader->Read<uint32_t>();
int index = pReader->Read<int>();
int spellBar = pReader->Read<int>();
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.AddSpellFavorite(spellID, index, spellBar);
break;
}
case REMOVE_FROM_SPELLBAR: // Remove item from spell bar
{
uint32_t spellID = pReader->Read<uint32_t>();
int spellBar = pReader->Read<int>();
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.RemoveSpellFavorite(spellID, spellBar);
break;
}
case PING: // Ping
{
Ping();
break;
}
case TRADE_OPEN: // Open Trade Negotiations
{
if (m_pPlayer->GetTradeManager() != NULL)
{
//already trading
return;
}
uint32_t target = pReader->Read<uint32_t>();
if (pReader->GetLastError())
return;
CWeenieObject *pOther = g_pWorld->FindWithinPVS(m_pPlayer, target);
CPlayerWeenie *pTarget;
if (pOther)
pTarget = pOther->AsPlayer();
if (!pOther || !pTarget)
{
// cannot open trade
m_pPlayer->SendText("Unable to open trade.", LTT_ERROR);
}
else if (pTarget->_playerModule.options_ & 0x20000)
{
SendText((pTarget->GetName() + " has disabled trading.").c_str(), LTT_ERROR);
}
else if (pTarget->IsBusyOrInAction())
{
SendText((pTarget->GetName() + " is busy.").c_str(), LTT_ERROR);
}
else if (pTarget->GetTradeManager() != NULL)
{
SendText((pTarget->GetName() + " is already trading with someone else!").c_str(), LTT_ERROR);
}
else
{
CTradeUseEvent *tradeEvent = new CTradeUseEvent;
tradeEvent->_target_id = pTarget->GetID();
tradeEvent->_max_use_distance = 1.0;
m_pPlayer->ExecuteUseEvent(tradeEvent);
}
break;
}
case TRADE_CLOSE: // Close Trade Negotiations
{
TradeManager* tm = m_pPlayer->GetTradeManager();
if (tm)
{
tm->CloseTrade(m_pPlayer);
return;
}
break;
}
case TRADE_ADD: // AddToTrade
{
TradeManager* tm = m_pPlayer->GetTradeManager();
if (tm)
{
uint32_t item = pReader->Read<uint32_t>();
tm->AddToTrade(m_pPlayer, item);
}
break;
}
case TRADE_ACCEPT: // Accept trade
{
TradeManager* tm = m_pPlayer->GetTradeManager();
if (tm)
{
tm->AcceptTrade(m_pPlayer);
}
break;
}
case TRADE_DECLINE: // Decline trade
{
TradeManager* tm = m_pPlayer->GetTradeManager();
if (tm)
{
tm->DeclineTrade(m_pPlayer);
}
break;
}
case TRADE_RESET: // Reset trade
{
TradeManager* tm = m_pPlayer->GetTradeManager();
if (tm)
{
tm->ResetTrade(m_pPlayer);
}
break;
}
case CLEAR_PLAYER_CONSENT_LIST: // Clears the player's corpse looting consent list, /consent clear
{
m_pPlayer->ClearConsent(false);
break;
}
case DISPLAY_PLAYER_CONSENT_LIST: // Display the player's corpse looting consent list, /consent who
{
m_pPlayer->DisplayConsent();
break;
}
case REMOVE_FROM_PLAYER_CONSENT_LIST: // Remove your corpse looting permission for the given player, / consent remove
{
std::string targetName = pReader->ReadString();
if (pReader->GetLastError())
break;
CPlayerWeenie *target = g_pWorld->FindPlayer(targetName.c_str());
if (target)
{
m_pPlayer->RemoveConsent(target);
}
break;
}
case ADD_PLAYER_PERMISSION: // Grants a player corpse looting permission, /permit add
{
std::string targetName = pReader->ReadString();
if (pReader->GetLastError())
break;
CPlayerWeenie *target = g_pWorld->FindPlayer(targetName.c_str());
if (target && target->GetCharacterOptions() & AcceptLootPermits_CharacterOption)
{
m_pPlayer->AddCorpsePermission(target);
}
break;
}
case REMOVE_PLAYER_PERMISSION:
{
std::string targetName = pReader->ReadString();
if (pReader->GetLastError())
break;
CPlayerWeenie *target = g_pWorld->FindPlayer(targetName.c_str());
if (target && target->GetCharacterOptions() & AcceptLootPermits_CharacterOption)
{
m_pPlayer->RemoveCorpsePermission(target);
}
break;
}
case HOUSE_BUY: //House_BuyHouse
{
uint32_t slumlord = pReader->Read<uint32_t>();
// TODO sanity check on the number of items here
PackableList<uint32_t> items;
items.UnPack(pReader);
if (pReader->GetLastError())
break;
HouseBuy(slumlord, items);
break;
}
case HOUSE_ABANDON: //House_AbandonHouse
{
HouseAbandon();
break;
}
case HOUSE_OF_PLAYER_QUERY: //House_QueryHouse
{
HouseRequestData();
break;
}
case HOUSE_RENT: //House_RentHouse
{
uint32_t slumlord = pReader->Read<uint32_t>();
// TODO sanity check on the number of items here
PackableList<uint32_t> items;
items.UnPack(pReader);
if (pReader->GetLastError())
break;
//HouseRent(slumlord, items);
break;
}
case DESIRED_COMPS_SET:
{
uint32_t compWCID = pReader->Read<uint32_t>();
uint32_t compQTY = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.AddOrUpdateDesiredComp(compWCID, compQTY);
break;
}
case HOUSE_ADD_GUEST: //House_AddPermanentGuest
{
std::string name = pReader->ReadString();
if (pReader->GetLastError())
break;
HouseAddPersonToAccessList(name);
break;
}
case HOUSE_REMOVE_GUEST: //House_RemovePermanentGuest
{
std::string name = pReader->ReadString();
if (pReader->GetLastError())
break;
HouseRemovePersonFromAccessList(name);
break;
}
case HOUSE_SET_OPEN_ACCESS: //House_SetOpenHouseStatus
{
int newSetting = pReader->Read<int>();
if (pReader->GetLastError())
break;
HouseToggleOpenAccess(newSetting > 0);
break;
}
case HOUSE_CHANGE_STORAGE_PERMISSIONS: //House_ChangeStoragePermission
{
std::string name = pReader->ReadString();
int isAdd = pReader->Read<int>();
if (pReader->GetLastError())
break;
HouseAddOrRemovePersonToStorageList(name, isAdd > 0);
break;
}
case HOUSE_CLEAR_STORAGE_PERMISSIONS: //House_RemoveAllStoragePermission
{
HouseClearStorageAccess();
break;
}
case HOUSE_GUEST_LIST: //House_RequestFullGuestList
{
HouseRequestAccessList();
break;
}
case ALLEGIANCE_SET_MOTD:
{
MAllegianceMOTDSet_0254 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_QUERY_MOTD: //Request allegiance MOTD
{
MAllegianceMOTDQuery_0255 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_CLEAR_MOTD:
{
MAllegianceMOTDClear_0256 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case HOUSE_QUERY_SLUMLORD:
{
// read in: uint32_t lord = pReader->ReadUInt32();
}
case HOUSE_SET_OPEN_STORAGE_ACCESS: //House_AddAllStoragePermission
{
HouseToggleOpenStorageAccess();
break;
}
case HOUSE_REMOVE_ALL_GUESTS: //House_RemoveAllPermanentGuests
{
HouseClearAccessList();
break;
}
case HOUSE_BOOT_ALL:
{
break;
}
case RECALL_HOUSE: // House Recall
{
HouseRecall();
break;
}
case ITEM_MANA_REQUEST: // Request Item Mana
{
uint32_t itemId = pReader->ReadUInt32();
if (pReader->GetLastError())
break;
m_pPlayer->HandleItemManaRequest(itemId);
m_pPlayer->_nextHeartBeatEmote = Timer::cur_time + 30.0;
break;
}
case HOUSE_SET_HOOKS_VISIBILITY: // House_SetHooksVisibility
{
int newSetting = pReader->Read<int>();
if (pReader->GetLastError())
break;
HouseToggleHooks(newSetting > 0);
break;
}
case HOUSE_CHANGE_ALLEGIANCE_GUEST_PERMISSIONS: //House_ModifyAllegianceGuestPermission
{
int newSetting = pReader->Read<int>();
if (pReader->GetLastError())
break;
HouseAddOrRemoveAllegianceToAccessList(newSetting > 0);
break;
}
case HOUSE_CHANGE_ALLEGIANCE_STORAGE_PERMISSIONS: //House_ModifyAllegianceStoragePermission
{
int newSetting = pReader->Read<int>();
if (pReader->GetLastError())
break;
HouseAddOrRemoveAllegianceToStorageList(newSetting > 0);
break;
}
case CHESS_JOIN: // Disabling until dev returns to update
{
//auto const guid = pReader->Read<uint32_t>();
//pReader->Read<uint32_t>(); // teamId, not used
//CWeenieObject* object = g_pWorld->FindObject(guid);
//if (!object)
// break;
//sChessManager->Join(m_pPlayer, object);
break;
}
case CHESS_QUIT: // Disabling until dev returns to update
//sChessManager->Quit(m_pPlayer);
break;
case CHESS_MOVE: // Disabling until dev returns to update
{
//GDLE::Chess::ChessPieceCoord from, to;
//from.UnPack(pReader);
//to.UnPack(pReader);
//sChessManager->Move(m_pPlayer, from, to);
break;
}
case CHESS_PASS: // Disabling until dev returns to update
//sChessManager->MovePass(m_pPlayer);
break;
case CHESS_STALEMATE: // Disabling until dev returns to update
{
//auto const on = pReader->Read<uint32_t>();
//sChessManager->Stalemate(m_pPlayer, on);
break;
}
case HOUSE_LIST_AVAILABLE:
{
// Read: HouseType houseType - type of house to list, cottage 1, villa 2, mansion 3, apartment 4
// Reply: 0x0271, HouseType houseType, PackableList<uint> houses, int numHouses
uint32_t houseType = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
GetHousesAvailable(houseType);
break;
}
case ALLEGIANCE_BOOT_PLAYER: // Boots a player from the allegiance, optionally all characters on their account
{
MAllegianceBoot_0277 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case RECALL_HOUSE_MANSION: // House_TeleToMansion
{
HouseMansionRecall();
break;
}
case DIE_COMMAND: // "/die" command
{
if (m_pPlayer->IsInPortalSpace())
{
m_pPlayer->NotifyWeenieError(WERROR_ACTIONS_LOCKED);
break;
}
if (!m_pPlayer->IsDead() && !m_pPlayer->IsInPortalSpace() && m_pPlayer->_deathTimer < 0)
{
m_pPlayer->UpdatePKActivity();
m_pPlayer->_deathTimer = Timer::cur_time + 10.5;
m_pPlayer->_dieTextTimer = Timer::cur_time + 2.0;
}
break;
}
case ALLEGIANCE_INFO_REQUEST: // allegiance info request
{
MAllegianceInfoRequest_027B msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case SPELLBOOK_FILTERS: // "/die" command
{
uint32_t filters = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
m_pPlayer->_playerModule.spell_filters_ = filters;
break;
}
case RECALL_MARKET: // Marketplace Recall
{
MarketplaceRecall();
break;
}
case PKLITE:
{
if (g_pConfig->PKOnly())
{
m_pPlayer->NotifyWeenieError(WERROR_PK_INVALID_STATUS);
m_pPlayer->SendText("You cannot be PKLite on this server", LTT_ERROR);
}
else
PKLiteEnable();
break;
}
case FELLOW_ASSIGN_NEW_LEADER: // "Fellowship Assign New Leader"
{
uint32_t target_id = pReader->Read<uint32_t>();
if (pReader->GetLastError())
break;
TryFellowshipAssignNewLeader(target_id);
break;
}
case FELLOW_CHANGE_OPENNESS: // "Fellowship Change Openness"
{
int open = pReader->Read<int>();
if (pReader->GetLastError())
break;
TryFellowshipChangeOpenness(open);
break;
}
case ALLEGIANCE_CHAT_BOOT:
{
MAllegianceChatBoot_02A0 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_ADD_PLAYER_BAN:
{
//MAllegianceBanAdd_02A1 msg(m_pPlayer);
//msg.Parse(pReader);
break;
}
case ALLEGIANCE_REMOVE_PLAYER_BAN:
{
//MAllegianceBanRemove_02A2 msg(m_pPlayer);
//msg.Parse(pReader);
break;
}
case ALLEGIANCE_LIST_BANS:
{
//MAllegianceBanList_02A3 msg(m_pPlayer);
//msg.Parse(pReader);
break;
}
case ALLEGIANCE_REMOVE_OFFICER:
{
MAllegianceOfficerRemove_02A5 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_LIST_OFFICERS:
{
MAllegianceOfficersList_02A6 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case ALLEGIANCE_CLEAR_OFFICERS:
{
MAllegianceOfficersClear_02A7 msg(m_pPlayer);
msg.Parse(pReader);
break;
}
case RECALL_ALLEGIANCE_HOMETOWN: //Allegiance_RecallAllegianceHometown (bindstone)
{
AllegianceHometownRecall();
/*MAllegianceRecallHometown_02AB msg(m_pPlayer);
msg.Parse(pReader);*/
break;
}
case FINISH_BARBER:
{
//Read: DataID basePalette, DataID headObject, DataID headTexture, DataID defaultHeadTexture, DataID eyesTexture
// DataID defaultEyesTexture, DataID noseTexture, DataID defaultNoseTexture, DataID mouthTexture, DataID defaultMouthTexture
// DataID skinPalette, DataID hairPalette, DataID eyesPalette, DataID setupID, int option1, int option2
// option1 = toggle certain race options e.g. empyrean float or flaming head undead, option2 = not used?
uint32_t basePalette = pReader->Read<uint32_t>();
uint32_t headObject = pReader->Read<uint32_t>();
uint32_t headTexture = pReader->Read<uint32_t>();
uint32_t defaultHeadTexture = pReader->Read<uint32_t>();
uint32_t eyesTexture = pReader->Read<uint32_t>();
uint32_t defaultEyesTexture = pReader->Read<uint32_t>();
uint32_t noseTexture = pReader->Read<uint32_t>();
uint32_t defaultNoseTexture = pReader->Read<uint32_t>();
uint32_t mouthTexture = pReader->Read<uint32_t>();
uint32_t defaultMouthTexture = pReader->Read<uint32_t>();
uint32_t skinPalette = pReader->Read<uint32_t>();
uint32_t hairPalette = pReader->Read<uint32_t>();
uint32_t eyesPalette = pReader->Read<uint32_t>();
uint32_t setupID = pReader->Read<uint32_t>();
int option1 = pReader->Read<int>();
int option2 = pReader->Read<int>();
m_pPlayer->m_Qualities.SetDataID(HEAD_OBJECT_DID, headObject);
m_pPlayer->m_Qualities.SetDataID(EYES_TEXTURE_DID, eyesTexture);
m_pPlayer->m_Qualities.SetDataID(NOSE_TEXTURE_DID, noseTexture);
m_pPlayer->m_Qualities.SetDataID(MOUTH_TEXTURE_DID, mouthTexture);
m_pPlayer->m_Qualities.SetDataID(SKIN_PALETTE_DID, skinPalette);
m_pPlayer->m_Qualities.SetDataID(HAIR_PALETTE_DID, hairPalette);
m_pPlayer->m_Qualities.SetDataID(EYES_PALETTE_DID, eyesPalette);
m_pPlayer->m_Qualities.SetDataID(SETUP_DID, setupID);
if (m_pPlayer->m_Qualities.GetInt(HERITAGE_GROUP_INT, 0) == Empyrean_HeritageGroup && option1 == 0)
{
m_pPlayer->m_Qualities.SetDataID(MOTION_TABLE_DID, 0x9000207);
m_pPlayer->NotifyDIDStatUpdated(MOTION_TABLE_DID);
}
else if (m_pPlayer->m_Qualities.GetInt(HERITAGE_GROUP_INT, 0) == Empyrean_HeritageGroup && option1 == 1)
{
m_pPlayer->m_Qualities.SetDataID(MOTION_TABLE_DID, 0x900020D);
m_pPlayer->NotifyDIDStatUpdated(MOTION_TABLE_DID);
}
m_pPlayer->UpdateModel();
break;
}
case MOVEMENT_JUMP: // Jump Movement
{
float extent = pReader->Read<float>(); // extent
Vector jumpVelocity;
jumpVelocity.UnPack(pReader);
Position position;
position.UnPack(pReader);
if (pReader->GetLastError())
break;
// CTransition *transition = m_pPlayer->transition(&m_pPlayer->m_Position, &position, 0);
/*
CTransition *transit = m_pPlayer->transition(&m_pPlayer->m_Position, &position, 0);
if (transit)
{
m_pPlayer->SetPositionInternal(transit);
}
*/
/*
double dist = m_pPlayer->m_Position.distance(position);
if (dist >= 5)
{
m_pPlayer->_force_position_timestamp++;
m_pPlayer->Movement_UpdatePos();
m_pPlayer->SendText(csprintf("Correcting position due to jump %f", dist), LTT_DEFAULT);
}
*/
double dist = m_pPlayer->m_Position.distance(position);
if (dist >= 10)
{
// Snap them back to their previous position
m_pPlayer->_force_position_timestamp++;
}
else
{
//m_pPlayer->SetPositionSimple(&position, TRUE);
CTransition *transit = m_pPlayer->transition(&m_pPlayer->m_Position, &position, 0);
if (transit)
{
m_pPlayer->SetPositionInternal(transit);
}
}
// m_pPlayer->m_Position = position;
/*
int32_t stamina = 0;
m_pPlayer->get_minterp()->jump(extent, stamina);
if (stamina > 0)
{
unsigned int curStamina = m_pPlayer->GetStamina();
if (stamina > curStamina)
{
// should maybe change the extent? idk
m_pPlayer->SetStamina(0);
}
else
m_pPlayer->SetStamina(curStamina - stamina);
}
m_pPlayer->Animation_Jump(extent, jumpVelocity);
m_pPlayer->m_bAnimUpdate = TRUE;
*/
int32_t stamina = 0;
if (m_pPlayer->JumpStaminaCost(extent, stamina) && stamina > 0)
{
unsigned int curStamina = m_pPlayer->GetStamina();
if (stamina > curStamina)
{
// should maybe change the extent? idk
m_pPlayer->SetStamina(0);
}
else
m_pPlayer->SetStamina(curStamina - stamina);
}
m_pPlayer->transient_state &= ~((uint32_t)TransientState::CONTACT_TS);
m_pPlayer->transient_state &= ~((uint32_t)WATER_CONTACT_TS);
m_pPlayer->calc_acceleration();
m_pPlayer->set_on_walkable(FALSE);
m_pPlayer->set_local_velocity(jumpVelocity, 0);
/*
Vector localVel = m_pPlayer->get_local_physics_velocity();
Vector vel = m_pPlayer->m_velocityVector;
m_pPlayer->EmoteLocal(csprintf("Received jump with velocity %.1f %.1f %.1f (my lv: %.1f %.1f %.1f my v: %.1f %.1f %.1f)",
jumpVelocity.x, jumpVelocity.y, jumpVelocity.z,
localVel.x, localVel.y, localVel.z,
vel.x, vel.y, vel.z));
*/
m_pPlayer->Movement_UpdateVector();
break;
}
case MOVEMENT_MOVE_TO_STATE: // CM_Movement__Event_MoveToState (update vector movement?)
{
if (m_pPlayer->m_bChangingStance)
{
//DEBUG_DATA << "Player changing stance during 0xF61C. Ignoring.";
break;
}
MoveToStatePack moveToState;
moveToState.UnPack(pReader);
if (pReader->GetLastError())
{
SERVER_WARN << "Bad animation message!";
SERVER_WARN << pReader->GetDataStart() << pReader->GetDataLen();
break;
}
//if (is_newer_event_stamp(moveToState.server_control_timestamp, m_pPlayer->_server_control_timestamp))
//{
// LOG(Temp, Normal, "Old server control timestamp on 0xF61C. Ignoring.\n");
// break;
//}
if (is_newer_event_stamp(moveToState.teleport_timestamp, m_pPlayer->_teleport_timestamp))
{
//SERVER_WARN << "Old teleport timestamp on 0xF61C. Ignoring.";
break;
}
if (is_newer_event_stamp(moveToState.force_position_ts, m_pPlayer->_force_position_timestamp))
{
//SERVER_WARN << "Old force position timestamp on 0xF61C. Ignoring.";
break;
}
if (m_pPlayer->IsDead())
{
SERVER_WARN << "Dead players can't move. Ignoring.";
break;
}
/*
CTransition *transit = m_pPlayer->transition(&m_pPlayer->m_Position, &moveToState.position, 0);
if (transit)
{
m_pPlayer->SetPositionInternal(transit);
}
*/
/*
double dist = m_pPlayer->m_Position.distance(moveToState.position);
if (dist >= 5)
{
m_pPlayer->_force_position_timestamp++;
m_pPlayer->Movement_UpdatePos();
m_pPlayer->SendText(csprintf("Correcting position due to state %f", dist), LTT_DEFAULT);
}
*/
/*
bool bHasCell = m_pPlayer->cell ? true : false;
m_pPlayer->SetPositionSimple(&moveToState.position, TRUE);
if (!m_pPlayer->cell && bHasCell)
{
m_pPlayer->SendText("Damnet...", LTT_DEFAULT);
}
*/
double dist = m_pPlayer->m_Position.distance(moveToState.position);
if (dist >= 10)
{
// Snap them back to their previous position
m_pPlayer->_force_position_timestamp++;
}
else
{
//m_pPlayer->SetPositionSimple(&moveToState.position, TRUE);
CTransition *transit = m_pPlayer->transition(&m_pPlayer->m_Position, &moveToState.position, 0);
if (transit)
{
m_pPlayer->SetPositionInternal(transit);
}
}
// m_pPlayer->m_Position = moveToState.position; // should interpolate to this, but oh well
/*
if (moveToState.contact)
{
m_pPlayer->transient_state |= ((uint32_t)TransientState::CONTACT_TS);
}
else
{
m_pPlayer->transient_state &= ~((uint32_t)TransientState::CONTACT_TS);
m_pPlayer->transient_state &= ~((uint32_t)WATER_CONTACT_TS);
}
m_pPlayer->calc_acceleration();
m_pPlayer->set_on_walkable(moveToState.contact);
*/
m_pPlayer->get_minterp()->standing_longjump = moveToState.longjump_mode ? TRUE : FALSE;
m_pPlayer->last_move_was_autonomous = true;
m_pPlayer->cancel_moveto();
if (m_pPlayer->m_UseManager && m_pPlayer->m_UseManager->IsMoving())
m_pPlayer->m_UseManager->HandleMoveToDone(WERROR_INTERRUPTED);
if (!(moveToState.raw_motion_state.current_style & CM_Style) && moveToState.raw_motion_state.current_style)
{
SERVER_WARN << "Bad style received" << moveToState.raw_motion_state.current_style;
break;
}
if (moveToState.raw_motion_state.forward_command & CM_Action)
{
SERVER_WARN << "Bad forward command received" << moveToState.raw_motion_state.forward_command;
break;
}
if (moveToState.raw_motion_state.sidestep_command & CM_Action)
{
SERVER_WARN << "Bad sidestep command received" << moveToState.raw_motion_state.sidestep_command;
break;
}
if (moveToState.raw_motion_state.turn_command & CM_Action)
{
SERVER_WARN << "Bad turn command received" << moveToState.raw_motion_state.turn_command;
break;
}
CMotionInterp *minterp = m_pPlayer->get_minterp();
minterp->raw_state = moveToState.raw_motion_state;
minterp->apply_raw_movement(TRUE, minterp->motion_allows_jump(minterp->interpreted_state.forward_command != 0));
WORD newestActionStamp = m_MoveActionStamp;
// Cancel attack
if (((m_pPlayer->m_bCancelAttack && minterp->interpreted_state.forward_speed < 0) || (m_pPlayer->m_bCancelAttackDuplicate && minterp->interpreted_state.forward_speed < 0)) && !moveToState.longjump_mode)
{
// Client sends multiple movements with negative speed (backsteps) even after Cancel Attacks have been handled. This catches those extras.
if (!m_pPlayer->m_bCancelAttackDuplicate)
m_pPlayer->m_bCancelAttackDuplicate = true;
else if (!m_pPlayer->m_bCancelAttack)
m_pPlayer->m_bCancelAttackDuplicate = false;
m_pPlayer->DoForcedStopCompletely();
m_pPlayer->m_bCancelAttack = false;
}
for (const auto &actionNew : moveToState.raw_motion_state.actions)
{
if (m_pPlayer->get_minterp()->interpreted_state.GetNumActions() >= MAX_EMOTE_QUEUE)
break;
if (is_newer_event_stamp(newestActionStamp, actionNew.stamp))
{
uint32_t commandID = GetCommandID(actionNew.action);
if (!(commandID & CM_Action) || !(commandID & CM_ChatEmote))
{
SERVER_WARN << "Bad action received" << commandID;
continue;
}
MovementParameters params;
params.action_stamp = ++m_pPlayer->m_wAnimSequence;
params.autonomous = 1;
if (actionNew.speed < -1)
params.speed = -1;
else if (actionNew.speed > 1)
params.speed = 1;
else
params.speed = actionNew.speed;
m_pPlayer->get_minterp()->DoMotion(commandID, &params);
// minterp->interpreted_state.AddAction(ActionNode(actionNew.action, actionNew.speed, ++m_pPlayer->m_wAnimSequence, TRUE));
// newestActionStamp = actionNew.stamp;
// m_pPlayer->Animation_PlayEmote(actionNew.action, actionNew.speed);
}
}
m_MoveActionStamp = newestActionStamp;
// m_pPlayer->Movement_UpdatePos();
m_pPlayer->Animation_Update();
// m_pPlayer->m_bAnimUpdate = TRUE;
//m_pPlayer->Movement_UpdatePos();
break;
}
case MOVEMENT_DO_MOVEMENT_COMMAND:
{
// Read: uint motion, float speed, uint holdKey
break;
}
case MOVEMENT_TURN_TO:
{
// Read: TurnToEventPack ttep - set of turning data, not in client
break;
}
case MOVEMENT_STOP:
{
// Read: uint motion, uint holdKey
break;
}
case MOVEMENT_AUTONOMY_LEVEL:
{
// Read: uint autonomyLevel - Seems to be 0, 1 or 2. I think 0/1 is server controlled, 2 is client controlled
// Align to uint32_t boundary
break;
}
case MOVEMENT_AUTONOMOUS_POSITION: // Update Exact Position
{
Position position;
position.UnPack(pReader);
WORD instance = pReader->ReadUInt16();
if (pReader->GetLastError())
break;
if (instance != m_pPlayer->_instance_timestamp)
{
SERVER_WARN << "Bad instance.";
break;
}
WORD server_control_timestamp = pReader->ReadUInt16();
if (pReader->GetLastError())
break;
//if (is_newer_event_stamp(server_control_timestamp, m_pPlayer->_server_control_timestamp))
//{
// LOG(Temp, Normal, "Old server control timestamp. Ignoring.\n");
// break;
//}
WORD teleport_timestamp = pReader->ReadUInt16();
if (pReader->GetLastError())
break;
if (is_newer_event_stamp(teleport_timestamp, m_pPlayer->_teleport_timestamp))
{
//SERVER_WARN << "Old teleport timestamp. Ignoring.";
break;
}
WORD force_position_ts = pReader->ReadUInt16();
if (pReader->GetLastError())
break;
if (is_newer_event_stamp(force_position_ts, m_pPlayer->_force_position_timestamp))
{
//SERVER_WARN << "Old force position timestamp. Ignoring.";
break;
}
BOOL bHasContact = pReader->ReadBYTE() ? TRUE : FALSE;
if (pReader->GetLastError())
break;
double dist = m_pPlayer->m_Position.distance(position);
if (dist >= 25)
{
// Snap them back to their previous position
m_pPlayer->_force_position_timestamp++;
// m_pPlayer->SendText(csprintf("Correcting position due to position update %f", dist), LTT_DEFAULT);
}
else
{
/*
CTransition *transit = m_pPlayer->transition(&m_pPlayer->m_Position, &position, 0);
if (transit)
{
m_pPlayer->SetPositionInternal(transit);
*/
/*
double distFromClient = m_pPlayer->m_Position.distance(position);
if (distFromClient >= 3.0)
{
m_pPlayer->_force_position_timestamp++;
}
}
*/
m_pPlayer->SetPositionSimple(&position, TRUE);
/*
if (!m_pPlayer->cell && bHasCell)
{
m_pPlayer->SendText("Damnet...", LTT_DEFAULT);
}
*/
// m_pPlayer->m_Position = position; // should interpolate this, not set this directly, but oh well
}
if (bHasContact)
{
m_pPlayer->transient_state |= ((uint32_t)TransientState::CONTACT_TS);
}
else
{
m_pPlayer->transient_state &= ~((uint32_t)TransientState::CONTACT_TS);
m_pPlayer->transient_state &= ~((uint32_t)WATER_CONTACT_TS);
}
m_pPlayer->calc_acceleration();
m_pPlayer->set_on_walkable(bHasContact);
m_pPlayer->Movement_UpdatePos();
break;
}
case MOVEMENT_JUMP_NON_AUTONOMOUS:
{
// Read: float extent - Power of jump
break;
}
default:
{
//Unknown Event
#ifdef _DEBUG
SERVER_WARN << "Unhandled client event" << dwEvent;
#endif
// LOG_BYTES(Temp, Verbose, in->GetDataPtr(), in->GetDataEnd() - in->GetDataPtr() );
}
}
}