diff --git a/changelog.txt b/changelog.txt index 4f6818041..faa8610cf 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/24/2014 == +Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) + == 09/23/2014 == Kayen: Spell recourse effects will now be applied AFTER the base spells effects have been applied (consistent with live). Kayen: SE_ApplySpell and SE_TriggerSpell will now be applied based on which effect slot they are used in (instead of always before all spell effects are checked). diff --git a/common/data_verification.h b/common/data_verification.h new file mode 100644 index 000000000..d42f72aac --- /dev/null +++ b/common/data_verification.h @@ -0,0 +1,43 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef COMMON_DATA_VERIFICATION_H +#define COMMON_DATA_VERIFICATION_H + +#include + +namespace EQEmu +{ + +template +T Clamp(const T& value, const T& lower, const T& upper) { + return std::max(lower, std::min(value, upper)); +} + +template +T ClampLower(const T& value, const T& lower) { + return std::max(lower, value); +} + +template +T ClampUpper(const T& value, const T& upper) { + return std::min(value, upper); +} + +} + +#endif diff --git a/common/database.cpp b/common/database.cpp index 356c7f6a8..1af4d05a0 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -924,9 +924,9 @@ bool Database::CheckDatabaseConversions() { int runconvert = 0; /* Check For Legacy Storage Method */ - std::string rquery = StringFormat("SELECT `profile` FROM `character_` LIMIT 1"); + std::string rquery = StringFormat("SHOW TABLES LIKE 'character_'"); auto results = QueryDatabase(rquery); - for (auto row = results.begin(); row != results.end(); ++row) { + if (results.RowCount() == 1){ runconvert = 1; printf("\n\n::: Legacy Character Data Binary Blob Storage Detected... \n"); printf("----------------------------------------------------------\n\n"); @@ -1857,7 +1857,7 @@ bool Database::CheckDatabaseConversions() { /* Run Tribute Convert */ first_entry = 0; rquery = ""; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 4294967295){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); first_entry = 1; @@ -2467,7 +2467,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename, u if(zonename == nullptr || strlen(zonename) == 0) return false; - std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); auto results = QueryDatabase(query); if (!results.Success()) { @@ -2486,7 +2486,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { } bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { - std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); auto results = QueryDatabase(query); if (!results.Success()) { diff --git a/common/emu_oplist.h b/common/emu_oplist.h index c1ceb3c80..af871ec6a 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -1,543 +1,545 @@ +// system use N(OP_ExploreUnknown), -N(OP_Heartbeat), -N(OP_ReloadUI), -N(OP_IncreaseStats), -N(OP_ApproveZone), -N(OP_Dye), -N(OP_Stamina), -N(OP_ControlBoat), -N(OP_MobUpdate), //not used anymore, here for lecacy reasons (eqextractor) -N(OP_ClientUpdate), -N(OP_ChannelMessage), -N(OP_SimpleMessage), -N(OP_FormattedMessage), -N(OP_TGB), -N(OP_Bind_Wound), -N(OP_Charm), -N(OP_Begging), -N(OP_MoveCoin), -N(OP_SpawnDoor), -N(OP_Sneak), -N(OP_ExpUpdate), -N(OP_DumpName), -N(OP_RespondAA), -N(OP_UpdateAA), -N(OP_SendAAStats), -N(OP_SendAATable), +// start (please add new opcodes in descending order and re-order any name changes where applicable) +N(OP_0x0193), +N(OP_0x0347), N(OP_AAAction), -N(OP_BoardBoat), -N(OP_LeaveBoat), -N(OP_AdventureInfoRequest), -N(OP_AdventureInfo), -N(OP_AdventureRequest), -N(OP_AdventureDetails), -N(OP_LDoNButton), +N(OP_AAExpUpdate), +N(OP_AcceptNewTask), +N(OP_AckPacket), +N(OP_Action), +N(OP_Action2), N(OP_AdventureData), +N(OP_AdventureDetails), N(OP_AdventureFinish), -N(OP_LeaveAdventure), +N(OP_AdventureInfo), +N(OP_AdventureInfoRequest), +N(OP_AdventureLeaderboardReply), +N(OP_AdventureLeaderboardRequest), +N(OP_AdventureMerchantPurchase), +N(OP_AdventureMerchantRequest), +N(OP_AdventureMerchantResponse), +N(OP_AdventureMerchantSell), +N(OP_AdventurePointsUpdate), +N(OP_AdventureRequest), +N(OP_AdventureStatsReply), +N(OP_AdventureStatsRequest), N(OP_AdventureUpdate), -N(OP_SendExpZonein), -N(OP_RaidUpdate), -N(OP_GuildLeader), -N(OP_GuildPeace), -N(OP_GuildRemove), -N(OP_GuildMemberList), -N(OP_GuildMemberUpdate), -N(OP_GuildMemberLevelUpdate), -N(OP_GuildInvite), -N(OP_GuildMOTD), -N(OP_SetGuildMOTD), -N(OP_GuildPublicNote), -N(OP_GetGuildsList), -N(OP_GuildDemote), -N(OP_GuildInviteAccept), -N(OP_GuildWar), -N(OP_GuildDelete), -N(OP_GuildManageRemove), -N(OP_GuildManageAdd), -N(OP_GuildManageStatus), -N(OP_GuildManageBanker), -N(OP_GetGuildMOTD), -N(OP_Trader), -N(OP_Bazaar), -N(OP_BecomeTrader), -N(OP_TraderItemUpdate), -N(OP_TraderShop), -N(OP_TraderBuy), -N(OP_PetCommands), -N(OP_TradeSkillCombine), +N(OP_AltCurrency), +N(OP_AltCurrencyMerchantReply), +N(OP_AltCurrencyMerchantRequest), +N(OP_AltCurrencyPurchase), +N(OP_AltCurrencyReclaim), +N(OP_AltCurrencySell), +N(OP_AltCurrencySellSelection), +N(OP_Animation), +N(OP_AnnoyingZoneUnknown), +N(OP_ApplyPoison), +N(OP_ApproveName), +N(OP_ApproveWorld), +N(OP_ApproveZone), +N(OP_Assist), +N(OP_AssistGroup), +N(OP_AugmentInfo), N(OP_AugmentItem), -N(OP_ItemName), -N(OP_ShopItem), -N(OP_ShopPlayerBuy), -N(OP_ShopPlayerSell), -N(OP_ShopDelItem), -N(OP_ShopRequest), -N(OP_ShopEnd), -N(OP_LFGCommand), -N(OP_LFGAppearance), -N(OP_GroupUpdate), -N(OP_GroupInvite), +N(OP_AutoAttack), +N(OP_AutoAttack2), +N(OP_AutoFire), +N(OP_Bandolier), +N(OP_BankerChange), +N(OP_Barter), +N(OP_Bazaar), +N(OP_BazaarInspect), +N(OP_BazaarSearch), +N(OP_BecomeCorpse), +N(OP_BecomeTrader), +N(OP_Begging), +N(OP_BeginCast), +N(OP_Bind_Wound), +N(OP_BlockedBuffs), +N(OP_BoardBoat), +N(OP_Buff), +N(OP_BuffCreate), +N(OP_BuffRemoveRequest), +N(OP_Bug), +N(OP_CameraEffect), +N(OP_Camp), +N(OP_CancelTask), +N(OP_CancelTrade), +N(OP_CastSpell), +N(OP_ChangeSize), +N(OP_ChannelMessage), +N(OP_CharacterCreate), +N(OP_CharacterCreateRequest), +N(OP_CharInventory), +N(OP_Charm), +N(OP_ChatMessage), +N(OP_ClearBlockedBuffs), +N(OP_ClearNPCMarks), +N(OP_ClearObject), +N(OP_ClearSurname), +N(OP_ClickDoor), +N(OP_ClickObject), +N(OP_ClickObjectAction), +N(OP_ClientError), +N(OP_ClientReady), +N(OP_ClientTimeStamp), +N(OP_ClientUpdate), +N(OP_CloseContainer), +N(OP_CloseTributeMaster), +N(OP_ColoredText), +N(OP_CombatAbility), +N(OP_Command), +N(OP_CompletedTasks), +N(OP_ConfirmDelete), +N(OP_Consent), +N(OP_ConsentDeny), +N(OP_ConsentResponse), +N(OP_Consider), +N(OP_ConsiderCorpse), +N(OP_Consume), +N(OP_ControlBoat), +N(OP_CorpseDrag), +N(OP_CorpseDrop), +N(OP_CrashDump), +N(OP_CrystalCountUpdate), +N(OP_CrystalCreate), +N(OP_CrystalReclaim), +N(OP_CustomTitles), +N(OP_Damage), +N(OP_Death), +N(OP_DelegateAbility), +N(OP_DeleteCharacter), +N(OP_DeleteCharge), +N(OP_DeleteItem), +N(OP_DeletePetition), +N(OP_DeleteSpawn), +N(OP_DeleteSpell), +N(OP_DenyResponse), +N(OP_Disarm), +N(OP_DisarmTraps), +N(OP_DisciplineTimer), +N(OP_DisciplineUpdate), +N(OP_DiscordMerchantInventory), +N(OP_DoGroupLeadershipAbility), +N(OP_DuelResponse), +N(OP_DuelResponse2), +N(OP_DumpName), +N(OP_Dye), +N(OP_DynamicWall), +N(OP_DzAddPlayer), +N(OP_DzChooseZone), +N(OP_DzCompass), +N(OP_DzExpeditionEndsWarning), +N(OP_DzExpeditionInfo), +N(OP_DzExpeditionList), +N(OP_DzJoinExpeditionConfirm), +N(OP_DzJoinExpeditionReply), +N(OP_DzLeaderStatus), +N(OP_DzListTimers), +N(OP_DzMakeLeader), +N(OP_DzMemberList), +N(OP_DzMemberStatus), +N(OP_DzPlayerList), +N(OP_DzQuit), +N(OP_DzRemovePlayer), +N(OP_DzSwapPlayer), +N(OP_Emote), +N(OP_EndLootRequest), +N(OP_EnduranceUpdate), +N(OP_EnterChat), +N(OP_EnterWorld), +N(OP_EnvDamage), +N(OP_ExpansionInfo), +N(OP_ExpUpdate), +N(OP_FaceChange), +N(OP_Feedback), +N(OP_FeignDeath), +N(OP_FellowshipUpdate), +N(OP_FindPersonReply), +N(OP_FindPersonRequest), +N(OP_FinishTrade), +N(OP_FinishWindow), +N(OP_FinishWindow2), +N(OP_Fishing), +N(OP_FloatListThing), +N(OP_Forage), +N(OP_ForceFindPerson), +N(OP_FormattedMessage), +N(OP_FriendsWho), +N(OP_GetGuildMOTD), +N(OP_GetGuildMOTDReply), +N(OP_GetGuildsList), +N(OP_GiveMoney), +N(OP_GMApproval), +N(OP_GMBecomeNPC), +N(OP_GMDelCorpse), +N(OP_GMEmoteZone), +N(OP_GMEndTraining), +N(OP_GMEndTrainingResponse), +N(OP_GMFind), +N(OP_GMGoto), +N(OP_GMHideMe), +N(OP_GMKick), +N(OP_GMKill), +N(OP_GMLastName), +N(OP_GMNameChange), +N(OP_GMSearchCorpse), +N(OP_GMServers), +N(OP_GMSummon), +N(OP_GMToggle), +N(OP_GMTraining), +N(OP_GMTrainSkill), +N(OP_GMTrainSkillConfirm), +N(OP_GMZoneRequest), +N(OP_GMZoneRequest2), +N(OP_GroundSpawn), +N(OP_GroupAcknowledge), +N(OP_GroupCancelInvite), +N(OP_GroupDelete), N(OP_GroupDisband), -N(OP_GroupInvite2), +N(OP_GroupDisbandOther), +N(OP_GroupDisbandYou), N(OP_GroupFollow), N(OP_GroupFollow2), -N(OP_GroupCancelInvite), -N(OP_CustomTitles), -N(OP_Split), -N(OP_Jump), -N(OP_ConsiderCorpse), -N(OP_SkillUpdate), -N(OP_GMEndTrainingResponse), -N(OP_GMEndTraining), -N(OP_GMTrainSkill), -N(OP_GMTraining), -N(OP_DeleteItem), -N(OP_CombatAbility), -N(OP_TrackUnknown), -N(OP_TrackTarget), -N(OP_Track), +N(OP_GroupInvite), +N(OP_GroupInvite2), +N(OP_GroupLeaderChange), +N(OP_GroupLeadershipAAUpdate), +N(OP_GroupMakeLeader), +N(OP_GroupRoles), +N(OP_GroupUpdate), +N(OP_GroupUpdateB), +N(OP_GroupUpdateLeaderAA), +N(OP_GuildBank), +N(OP_GuildCreate), +N(OP_GuildDelete), +N(OP_GuildDemote), +N(OP_GuildInvite), +N(OP_GuildInviteAccept), +N(OP_GuildLeader), +N(OP_GuildManageAdd), +N(OP_GuildManageBanker), +N(OP_GuildManageRemove), +N(OP_GuildManageStatus), +N(OP_GuildMemberLevelUpdate), +N(OP_GuildMemberList), +N(OP_GuildMemberUpdate), +N(OP_GuildMOTD), +N(OP_GuildPeace), +N(OP_GuildPromote), +N(OP_GuildPublicNote), +N(OP_GuildRemove), +N(OP_GuildsList), +N(OP_GuildStatus), +N(OP_GuildTributeInfo), +N(OP_GuildUpdateURLAndChannel), +N(OP_GuildWar), +N(OP_Heartbeat), +N(OP_Hide), +N(OP_HideCorpse), +N(OP_HPUpdate), +N(OP_Illusion), +N(OP_IncreaseStats), +N(OP_InitialHPUpdate), +N(OP_InitialMobHealth), +N(OP_InspectAnswer), +N(OP_InspectMessageUpdate), +N(OP_InspectRequest), +N(OP_InstillDoubt), +N(OP_InterruptCast), N(OP_ItemLinkClick), N(OP_ItemLinkResponse), N(OP_ItemLinkText), -N(OP_RezzAnswer), -N(OP_RezzComplete), -N(OP_SendZonepoints), -N(OP_SetRunMode), -N(OP_InspectRequest), -N(OP_InspectAnswer), -N(OP_SenseTraps), -N(OP_DisarmTraps), -N(OP_Assist), -N(OP_AssistGroup), -N(OP_PickPocket), -N(OP_LootRequest), -N(OP_EndLootRequest), -N(OP_MoneyOnCorpse), -N(OP_LootComplete), -N(OP_LootItem), -N(OP_MoveItem), -N(OP_WhoAllRequest), -N(OP_WhoAllResponse), -N(OP_Consume), -N(OP_AutoAttack), -N(OP_AutoAttack2), -N(OP_TargetMouse), -N(OP_TargetCommand), -N(OP_TargetReject), -N(OP_TargetHoTT), -N(OP_Hide), -N(OP_Forage), -N(OP_Fishing), -N(OP_Bug), -N(OP_Emote), -N(OP_Consider), -N(OP_FaceChange), -N(OP_RandomReq), -N(OP_RandomReply), -N(OP_Camp), -N(OP_YellForHelp), -N(OP_SafePoint), -N(OP_Buff), -N(OP_BuffFadeMsg), -N(OP_SpecialMesg), -N(OP_Consent), -N(OP_ConsentResponse), -N(OP_Stun), -N(OP_BeginCast), -N(OP_CastSpell), -N(OP_InterruptCast), -N(OP_Death), -N(OP_FeignDeath), -N(OP_Illusion), -N(OP_LevelUpdate), -N(OP_LevelAppearance), -N(OP_MemorizeSpell), -N(OP_HPUpdate), -N(OP_Mend), -N(OP_Taunt), -N(OP_GMDelCorpse), -N(OP_GMFind), -N(OP_GMServers), -N(OP_GMGoto), -N(OP_GMSummon), -N(OP_GMKill), -N(OP_GMLastName), -N(OP_GMToggle), -N(OP_GMEmoteZone), -N(OP_GMBecomeNPC), -N(OP_GMHideMe), -N(OP_GMZoneRequest), -N(OP_GMZoneRequest2), -N(OP_Petition), -N(OP_PetitionRefresh), -N(OP_PDeletePetition), -N(OP_PetitionBug), -N(OP_PetitionUpdate), -N(OP_PetitionCheckout), -N(OP_PetitionCheckout2), -N(OP_PetitionDelete), -N(OP_PetitionResolve), -N(OP_PetitionCheckIn), -N(OP_PetitionUnCheckout), -N(OP_PetitionQue), -N(OP_SetServerFilter), -N(OP_NewSpawn), -N(OP_Animation), -N(OP_ZoneChange), -N(OP_DeleteSpawn), -N(OP_EnvDamage), -N(OP_Action), -N(OP_Damage), -N(OP_ManaChange), -N(OP_ClientError), -N(OP_Save), -N(OP_LocInfo), -N(OP_Surname), -N(OP_ClearSurname), -N(OP_SwapSpell), -N(OP_DeleteSpell), -N(OP_CloseContainer), -N(OP_ClickObjectAction), -N(OP_GroundSpawn), -N(OP_ClearObject), -N(OP_ZoneUnavail), +N(OP_ItemName), N(OP_ItemPacket), -N(OP_TradeRequest), -N(OP_TradeRequestAck), -N(OP_TradeAcceptClick), -N(OP_TradeMoneyUpdate), -N(OP_TradeCoins), -N(OP_CancelTrade), -N(OP_FinishTrade), -N(OP_SaveOnZoneReq), -N(OP_Logout), -N(OP_LogoutReply), -N(OP_PreLogoutReply), -N(OP_DuelResponse2), -N(OP_InstillDoubt), -N(OP_SafeFallSuccess), -N(OP_DisciplineUpdate), -N(OP_SendGuildTributes), -N(OP_SendTributes), -N(OP_TributeUpdate), -N(OP_TributeItem), -N(OP_TributePointUpdate), -N(OP_TributeInfo), -N(OP_GuildTributeInfo), -N(OP_OpenGuildTributeMaster), -N(OP_OpenTributeMaster), -N(OP_TributeTimer), -N(OP_SelectTribute), -N(OP_TributeNPC), -N(OP_TributeMoney), -N(OP_TributeToggle), -N(OP_CloseTributeMaster), -N(OP_RecipesFavorite), -N(OP_RecipesSearch), -N(OP_RecipeReply), -N(OP_RecipeDetails), -N(OP_RecipeAutoCombine), -N(OP_Shielding), -N(OP_FindPersonRequest), -N(OP_FindPersonReply), -N(OP_ZoneEntry), -N(OP_PlayerProfile), -N(OP_CharInventory), -N(OP_ZoneSpawns), -N(OP_Weather), -N(OP_ReqNewZone), -N(OP_NewZone), -N(OP_ReqClientSpawn), -N(OP_SpawnAppearance), -N(OP_ClientReady), -N(OP_ZoneComplete), -N(OP_ApproveWorld), -N(OP_LogServer), -N(OP_MOTD), -N(OP_SendLoginInfo), -N(OP_DeleteCharacter), -N(OP_SendCharInfo), -N(OP_ExpansionInfo), -N(OP_CharacterCreate), -N(OP_CharacterCreateRequest), -N(OP_RandomNameGenerator), -N(OP_GuildsList), -N(OP_ApproveName), -N(OP_EnterWorld), -N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted -N(OP_SendSystemStats), -N(OP_World_Client_CRC1), -N(OP_World_Client_CRC2), -N(OP_SetChatServer), -N(OP_SetChatServer2), -N(OP_ZoneServerInfo), -N(OP_WorldClientReady), -N(OP_WorldUnknown001), -N(OP_AckPacket), -N(OP_WearChange), -N(OP_CrashDump), -N(OP_LoginComplete), -N(OP_GMNameChange), -N(OP_ReadBook), -N(OP_GMKick), -N(OP_RezzRequest), -N(OP_MultiLineMsg), -N(OP_TimeOfDay), -N(OP_CompletedTasks), -N(OP_MoneyUpdate), -N(OP_ClickObject), -N(OP_MoveDoor), -N(OP_TraderDelItem), -N(OP_AdventureMerchantPurchase), -N(OP_TestBuff), -N(OP_DuelResponse), -N(OP_RequestDuel), -N(OP_BazaarInspect), -N(OP_ClickDoor), -N(OP_GroupAcknowledge), -N(OP_GroupDelete), -N(OP_AdventureMerchantResponse), -N(OP_ShopEndConfirm), -N(OP_AdventureMerchantRequest), -N(OP_Sound), -N(OP_0x0193), -N(OP_0x0347), -N(OP_WorldComplete), -N(OP_MobRename), -N(OP_TaskDescription), -N(OP_TaskActivity), -N(OP_TaskMemberList), -N(OP_AnnoyingZoneUnknown), -N(OP_Some3ByteHPUpdate), -N(OP_FloatListThing), -N(OP_AAExpUpdate), -N(OP_ForceFindPerson), -N(OP_PlayMP3), -N(OP_RequestClientZoneChange), -N(OP_SomeItemPacketMaybe), -N(OP_QueryResponseThing), -N(OP_Some6ByteHPUpdate), -N(OP_BankerChange), -N(OP_BecomeCorpse), -N(OP_Action2), -N(OP_BazaarSearch), -N(OP_SetTitle), -N(OP_SetTitleReply), -N(OP_ConfirmDelete), -N(OP_ConsentDeny), -N(OP_CrystalCountUpdate), -N(OP_DeletePetition), -N(OP_DenyResponse), -N(OP_Disarm), -N(OP_Feedback), -N(OP_FriendsWho), -N(OP_GMApproval), -N(OP_GMSearchCorpse), -N(OP_GuildBank), -N(OP_InitialHPUpdate), -N(OP_InitialMobHealth), +N(OP_ItemPreview), +N(OP_ItemVerifyReply), +N(OP_ItemVerifyRequest), +N(OP_ItemViewUnknown), +N(OP_Jump), +N(OP_KeyRing), +N(OP_KnowledgeBase), +N(OP_LDoNButton), +N(OP_LDoNDisarmTraps), +N(OP_LDoNInspect), +N(OP_LDoNOpen), +N(OP_LDoNPickLock), +N(OP_LDoNSenseTraps), +N(OP_LeadershipExpToggle), +N(OP_LeadershipExpUpdate), +N(OP_LeaveAdventure), +N(OP_LeaveBoat), +N(OP_LevelAppearance), +N(OP_LevelUpdate), +N(OP_LFGAppearance), +N(OP_LFGCommand), N(OP_LFGGetMatchesRequest), N(OP_LFGGetMatchesResponse), N(OP_LFGResponse), +N(OP_LFGuild), N(OP_LFPCommand), N(OP_LFPGetMatchesRequest), N(OP_LFPGetMatchesResponse), -N(OP_LeadershipExpToggle), -N(OP_LeadershipExpUpdate), N(OP_LoadSpellSet), +N(OP_LocInfo), N(OP_LockoutTimerInfo), +N(OP_Login), +N(OP_LoginAccepted), +N(OP_LoginComplete), +N(OP_LoginUnknown1), +N(OP_LoginUnknown2), +N(OP_Logout), +N(OP_LogoutReply), +N(OP_LogServer), +N(OP_LootComplete), +N(OP_LootItem), +N(OP_LootRequest), +N(OP_ManaChange), +N(OP_ManaUpdate), +N(OP_MarkNPC), +N(OP_Marquee), +N(OP_MemorizeSpell), +N(OP_Mend), N(OP_MendHPUpdate), +N(OP_MercenaryAssign), +N(OP_MercenaryCommand), +N(OP_MercenaryDataRequest), +N(OP_MercenaryDataResponse), +N(OP_MercenaryDataUpdate), +N(OP_MercenaryDataUpdateRequest), +N(OP_MercenaryDismiss), +N(OP_MercenaryHire), +N(OP_MercenarySuspendRequest), +N(OP_MercenarySuspendResponse), +N(OP_MercenaryTimer), +N(OP_MercenaryTimerRequest), +N(OP_MercenaryUnknown1), +N(OP_MercenaryUnsuspendResponse), +N(OP_MobEnduranceUpdate), N(OP_MobHealth), +N(OP_MobManaUpdate), +N(OP_MobRename), +N(OP_MobUpdate), // not used anymore, here for lecacy reasons (eqextractor) +N(OP_MoneyOnCorpse), +N(OP_MoneyUpdate), +N(OP_MOTD), +N(OP_MoveCoin), +N(OP_MoveDoor), +N(OP_MoveItem), N(OP_MoveLogDisregard), N(OP_MoveLogRequest), +N(OP_MultiLineMsg), +N(OP_NewSpawn), +N(OP_NewTitlesAvailable), +N(OP_NewZone), +N(OP_OnLevelMessage), +N(OP_OpenContainer), +N(OP_OpenDiscordMerchant), +N(OP_OpenGuildTributeMaster), +N(OP_OpenInventory), +N(OP_OpenNewTasksWindow), +N(OP_OpenTributeMaster), +N(OP_PDeletePetition), +N(OP_PetBuffWindow), +N(OP_PetCommands), +N(OP_Petition), +N(OP_PetitionBug), +N(OP_PetitionCheckIn), +N(OP_PetitionCheckout), +N(OP_PetitionCheckout2), +N(OP_PetitionDelete), +N(OP_PetitionQue), +N(OP_PetitionRefresh), +N(OP_PetitionResolve), N(OP_PetitionSearch), N(OP_PetitionSearchResults), N(OP_PetitionSearchText), -N(OP_RaidInvite), -N(OP_ReclaimCrystals), -N(OP_Report), -N(OP_SenseHeading), -N(OP_LDoNOpen), -N(OP_LDoNSenseTraps), -N(OP_LDoNPickLock), -N(OP_LDoNDisarmTraps), -N(OP_LDoNInspect), -N(OP_DynamicWall), -N(OP_RequestTitles), -N(OP_PurchaseLeadershipAA), -N(OP_UpdateLeadershipAA), -N(OP_AdventurePointsUpdate), -N(OP_ZoneInUnknown), -N(OP_ZoneServerReady), //terrible name. -N(OP_ZoneGuildList), -N(OP_SendTitleList), -N(OP_NewTitlesAvailable), -N(OP_Bandolier), -N(OP_OpenDiscordMerchant), -N(OP_DiscordMerchantInventory), -N(OP_GiveMoney), -N(OP_OnLevelMessage), -N(OP_RequestKnowledgeBase), -N(OP_KnowledgeBase), -N(OP_VetRewardsAvaliable), -N(OP_VetClaimRequest), -N(OP_VetClaimReply), -N(OP_WeaponEquip1), -N(OP_WeaponEquip2), -N(OP_WeaponUnequip2), -N(OP_WorldLogout), -N(OP_SessionReady), -//Login -N(OP_Login), -N(OP_ServerListRequest), +N(OP_PetitionUnCheckout), +N(OP_PetitionUpdate), +N(OP_PickPocket), +N(OP_PlayerProfile), N(OP_PlayEverquestRequest), -N(OP_ChatMessage), -N(OP_LoginAccepted), -N(OP_ServerListResponse), -N(OP_Poll), N(OP_PlayEverquestResponse), -N(OP_EnterChat), +N(OP_PlayMP3), +N(OP_Poll), N(OP_PollResponse), -N(OP_Command), -N(OP_ZonePlayerToBind), -N(OP_AutoFire), -N(OP_Rewind), -N(OP_OpenNewTasksWindow), -N(OP_TaskActivityComplete), -N(OP_AcceptNewTask), -N(OP_CancelTask), -N(OP_TaskHistoryRequest), -N(OP_TaskHistoryReply), -N(OP_PetBuffWindow), -N(OP_RaidJoin), -N(OP_Translocate), -N(OP_Sacrifice), -N(OP_KeyRing), N(OP_PopupResponse), -N(OP_DeleteCharge), +N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted N(OP_PotionBelt), -N(OP_Barter), -N(OP_VoiceMacroIn), -N(OP_VoiceMacroOut), -N(OP_WorldObjectsSent), -N(OP_BlockedBuffs), -N(OP_RemoveBlockedBuffs), -N(OP_ClearBlockedBuffs), -N(OP_GroupUpdateLeaderAA), -N(OP_MarkNPC), -N(OP_ClearNPCMarks), -N(OP_DoGroupLeadershipAbility), -N(OP_DelegateAbility), -N(OP_SetGroupTarget), -N(OP_ApplyPoison), -N(OP_FinishWindow), -N(OP_FinishWindow2), -N(OP_ItemVerifyRequest), -N(OP_ItemVerifyReply), -N(OP_GMTrainSkillConfirm), -N(OP_RestState), -N(OP_AugmentInfo), -N(OP_PVPStats), -N(OP_PVPLeaderBoardRequest), -N(OP_PVPLeaderBoardReply), -N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PreLogoutReply), +N(OP_PurchaseLeadershipAA), N(OP_PVPLeaderBoardDetailsReply), -N(OP_DisciplineTimer), -N(OP_RespawnWindow), -N(OP_AdventureMerchantSell), -N(OP_AdventureStatsRequest), -N(OP_AdventureStatsReply), -N(OP_AdventureLeaderboardRequest), -N(OP_AdventureLeaderboardReply), -N(OP_SetStartCity), -N(OP_LoginUnknown1), -N(OP_LoginUnknown2), -N(OP_ItemViewUnknown), -N(OP_GetGuildMOTDReply), -N(OP_SetGuildRank), -N(OP_SpawnPositionUpdate), -N(OP_ManaUpdate), -N(OP_EnduranceUpdate), -N(OP_MobManaUpdate), -N(OP_MobEnduranceUpdate), -N(OP_GroupUpdateB), -N(OP_GroupDisbandYou), -N(OP_GroupDisbandOther), -N(OP_GroupLeaderChange), -N(OP_GroupLeadershipAAUpdate), -N(OP_GroupRoles), -N(OP_SendFindableNPCs), -N(OP_HideCorpse), -N(OP_TargetBuffs), -N(OP_TradeBusy), -N(OP_GuildUpdateURLAndChannel), -N(OP_CameraEffect), -N(OP_SpellEffect), -N(OP_DzQuit), -N(OP_DzListTimers), -N(OP_DzPlayerList), -N(OP_DzAddPlayer), -N(OP_DzRemovePlayer), -N(OP_DzSwapPlayer), -N(OP_DzMakeLeader), -N(OP_DzJoinExpeditionConfirm), -N(OP_DzJoinExpeditionReply), -N(OP_DzExpeditionInfo), -N(OP_DzMemberStatus), -N(OP_DzLeaderStatus), -N(OP_DzExpeditionEndsWarning), -N(OP_DzExpeditionList), -N(OP_DzMemberList), -N(OP_DzCompass), -N(OP_DzChooseZone), -N(OP_BuffCreate), -N(OP_GuildStatus), -N(OP_BuffRemoveRequest), -N(OP_CorpseDrag), -N(OP_CorpseDrop), -N(OP_ChangeSize), -N(OP_GroupMakeLeader), +N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PVPLeaderBoardReply), +N(OP_PVPLeaderBoardRequest), +N(OP_PVPStats), +N(OP_QueryResponseThing), +N(OP_RaidInvite), +N(OP_RaidJoin), +N(OP_RaidUpdate), +N(OP_RandomNameGenerator), +N(OP_RandomReply), +N(OP_RandomReq), +N(OP_ReadBook), +N(OP_RecipeAutoCombine), +N(OP_RecipeDetails), +N(OP_RecipeReply), +N(OP_RecipesFavorite), +N(OP_RecipesSearch), +N(OP_ReclaimCrystals), +N(OP_ReloadUI), N(OP_RemoveAllDoors), +N(OP_RemoveBlockedBuffs), N(OP_RemoveNimbusEffect), -N(OP_GuildCreate), -N(OP_AltCurrency), -N(OP_FellowshipUpdate), -N(OP_AltCurrencyMerchantRequest), -N(OP_AltCurrencyMerchantReply), -N(OP_AltCurrencyPurchase), -N(OP_AltCurrencySellSelection), -N(OP_AltCurrencyReclaim), -N(OP_AltCurrencySell), -N(OP_Untargetable), -N(OP_CrystalReclaim), -N(OP_CrystalCreate), +N(OP_Report), +N(OP_ReqClientSpawn), +N(OP_ReqNewZone), +N(OP_RequestClientZoneChange), +N(OP_RequestDuel), +N(OP_RequestKnowledgeBase), +N(OP_RequestTitles), +N(OP_RespawnWindow), +N(OP_RespondAA), +N(OP_RestState), +N(OP_Rewind), +N(OP_RezzAnswer), +N(OP_RezzComplete), +N(OP_RezzRequest), +N(OP_Sacrifice), +N(OP_SafeFallSuccess), +N(OP_SafePoint), +N(OP_Save), +N(OP_SaveOnZoneReq), +N(OP_SelectTribute), +N(OP_SendAAStats), +N(OP_SendAATable), +N(OP_SendCharInfo), +N(OP_SendExpZonein), +N(OP_SendFindableNPCs), +N(OP_SendGuildTributes), +N(OP_SendLoginInfo), N(OP_SendMaxCharacters), N(OP_SendMembership), N(OP_SendMembershipDetails), -N(OP_LFGuild), +N(OP_SendSystemStats), +N(OP_SendTitleList), +N(OP_SendTributes), +N(OP_SendZonepoints), +N(OP_SenseHeading), +N(OP_SenseTraps), +N(OP_ServerListRequest), +N(OP_ServerListResponse), +N(OP_SessionReady), +N(OP_SetChatServer), +N(OP_SetChatServer2), +N(OP_SetGroupTarget), +N(OP_SetGuildMOTD), +N(OP_SetGuildRank), +N(OP_SetRunMode), +N(OP_SetServerFilter), +N(OP_SetStartCity), +N(OP_SetTitle), +N(OP_SetTitleReply), +N(OP_Shielding), +N(OP_ShopDelItem), +N(OP_ShopEnd), +N(OP_ShopEndConfirm), +N(OP_ShopItem), +N(OP_ShopPlayerBuy), +N(OP_ShopPlayerSell), +N(OP_ShopRequest), +N(OP_SimpleMessage), +N(OP_SkillUpdate), +N(OP_Sneak), +N(OP_Some3ByteHPUpdate), +N(OP_Some6ByteHPUpdate), +N(OP_SomeItemPacketMaybe), +N(OP_Sound), +N(OP_SpawnAppearance), +N(OP_SpawnDoor), +N(OP_SpawnPositionUpdate), +N(OP_SpecialMesg), +N(OP_SpellEffect), +N(OP_Split), +N(OP_Stamina), +N(OP_Stun), +N(OP_Surname), +N(OP_SwapSpell), +N(OP_TargetBuffs), +N(OP_TargetCommand), +N(OP_TargetHoTT), +N(OP_TargetMouse), +N(OP_TargetReject), +N(OP_TaskActivity), +N(OP_TaskActivityComplete), +N(OP_TaskDescription), +N(OP_TaskHistoryReply), +N(OP_TaskHistoryRequest), +N(OP_TaskMemberList), +N(OP_Taunt), +N(OP_TestBuff), +N(OP_TGB), +N(OP_TimeOfDay), +N(OP_Track), +N(OP_TrackTarget), +N(OP_TrackUnknown), +N(OP_TradeAcceptClick), +N(OP_TradeBusy), +N(OP_TradeCoins), +N(OP_TradeMoneyUpdate), +N(OP_Trader), +N(OP_TraderBuy), +N(OP_TraderDelItem), +N(OP_TradeRequest), +N(OP_TradeRequestAck), +N(OP_TraderItemUpdate), +N(OP_TraderShop), +N(OP_TradeSkillCombine), +N(OP_Translocate), +N(OP_TributeInfo), +N(OP_TributeItem), +N(OP_TributeMoney), +N(OP_TributeNPC), +N(OP_TributePointUpdate), +N(OP_TributeTimer), +N(OP_TributeToggle), +N(OP_TributeUpdate), +N(OP_Untargetable), +N(OP_UpdateAA), +N(OP_UpdateLeadershipAA), +N(OP_VetClaimReply), +N(OP_VetClaimRequest), +N(OP_VetRewardsAvaliable), +N(OP_VoiceMacroIn), +N(OP_VoiceMacroOut), +N(OP_WeaponEquip1), +N(OP_WeaponEquip2), +N(OP_WeaponUnequip2), +N(OP_WearChange), +N(OP_Weather), +N(OP_Weblink), +N(OP_WhoAllRequest), +N(OP_WhoAllResponse), +N(OP_World_Client_CRC1), +N(OP_World_Client_CRC2), +N(OP_WorldClientReady), +N(OP_WorldComplete), +N(OP_WorldLogout), +N(OP_WorldObjectsSent), +N(OP_WorldUnknown001), +N(OP_XTargetAutoAddHaters), N(OP_XTargetRequest), N(OP_XTargetResponse), -N(OP_XTargetAutoAddHaters), -N(OP_Weblink), -N(OP_InspectMessageUpdate), -N(OP_ItemPreview), -N(OP_MercenaryDataRequest), -N(OP_MercenaryDataResponse), -N(OP_MercenaryHire), -N(OP_MercenaryUnknown1), -N(OP_MercenaryTimer), -N(OP_MercenaryAssign), -N(OP_MercenaryDataUpdate), -N(OP_MercenaryCommand), -N(OP_MercenarySuspendRequest), -N(OP_MercenarySuspendResponse), -N(OP_MercenaryUnsuspendResponse), -N(OP_MercenaryDataUpdateRequest), -N(OP_MercenaryDismiss), -N(OP_MercenaryTimerRequest), -N(OP_OpenInventory), -N(OP_OpenContainer), -N(OP_Marquee), -N(OP_ClientTimeStamp), -N(OP_GuildPromote), +N(OP_YellForHelp), +N(OP_ZoneChange), +N(OP_ZoneComplete), +N(OP_ZoneEntry), +N(OP_ZoneGuildList), +N(OP_ZoneInUnknown), +N(OP_ZonePlayerToBind), +N(OP_ZoneServerInfo), +N(OP_ZoneServerReady), +N(OP_ZoneSpawns), +N(OP_ZoneUnavail), +// mail and chat opcodes located in ../mail_oplist.h diff --git a/common/eq_packet.h b/common/eq_packet.h index 9e0bcec3e..04418f733 100644 --- a/common/eq_packet.h +++ b/common/eq_packet.h @@ -97,16 +97,15 @@ protected: }; class EQApplicationPacket : public EQPacket { -// friend class EQProtocolPacket; friend class EQStream; public: - EQApplicationPacket() : EQPacket(OP_Unknown,nullptr,0) + EQApplicationPacket() : EQPacket(OP_Unknown, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op) : EQPacket(op,nullptr,0) + EQApplicationPacket(const EmuOpcode op) : EQPacket(op, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op,nullptr,len) + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op, nullptr, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op,buf,len) + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op, buf, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } bool combine(const EQApplicationPacket *rhs); uint32 serialize (uint16 opcode, unsigned char *dest) const; @@ -119,12 +118,16 @@ public: virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + uint16 GetOpcodeBypass() { return opcode_bypass; } + void SetOpcodeBypass(uint16 v) { opcode_bypass = v; } + protected: uint8 app_opcode_size; + uint16 opcode_bypass; private: - EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size) { app_opcode_size = p.app_opcode_size; } + EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size), opcode_bypass(p.opcode_bypass) { app_opcode_size = p.app_opcode_size; } }; diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index dab4bf0bb..ea0acaa90 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4067,7 +4067,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 8cb67e0e0..7b69decc2 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -532,9 +532,12 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) return; } - uint16 opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); - - //_log(NET__APP_TRACE, "Queueing %sacked packet with opcode 0x%x (%s) and length %d", ack_req?"":"non-", opcode, OpcodeManager::EmuToName(pack->emu_opcode), pack->size); + uint16 opcode = 0; + if(pack->GetOpcodeBypass() != 0) { + opcode = pack->GetOpcodeBypass(); + } else { + opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); + } if (!ack_req) { NonSequencedPush(new EQProtocolPacket(opcode, pack->pBuffer, pack->size)); @@ -877,43 +880,6 @@ sockaddr_in address; AddBytesSent(length); } -/* -commented out since im not sure theres a lot of merit in it. -Really it was bitterness towards allocating a 2k buffer on the stack each call. -Im sure the thought was client side, but even then, they will -likely need a whole thread to call this method, in which case, they should -supply the buffer so we dont re-allocate it each time. -EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) -{ -int socklen; -int length=0; -EQProtocolPacket *p=nullptr; -char temp[15]; - - socklen=sizeof(sockaddr); -#ifdef _WINDOWS - length=recvfrom(eq_fd, (char *)_tempBuffer, 2048, 0, (struct sockaddr*)from, (int *)&socklen); -#else - length=recvfrom(eq_fd, _tempBuffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); -#endif - - if (length>=2) { - p=new EQProtocolPacket(_tempBuffer[1],&_tempBuffer[2],length-2); - - uint32 ip=from->sin_addr.s_addr; - sprintf(temp,"%d.%d.%d.%d:%d", - *(unsigned char *)&ip, - *((unsigned char *)&ip+1), - *((unsigned char *)&ip+2), - *((unsigned char *)&ip+3), - ntohs(from->sin_port)); - //std::cout << timestamp() << "Data from: " << temp << " OpCode 0x" << std::hex << std::setw(2) << std::setfill('0') << (int)p->opcode << std::dec << std::endl; - //dump_message(p->pBuffer,p->size,timestamp()); - - } - return p; -}*/ - void EQStream::SendSessionResponse() { EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,nullptr,sizeof(SessionResponse)); @@ -1101,14 +1067,6 @@ EQProtocolPacket *p=nullptr; SequencedQueue.clear(); } MOutboundQueue.unlock(); - -/*if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - _log(NET__ERROR, _L "Out-bound Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); -} -if(NextSequencedSend > SequencedQueue.size()) { - _log(NET__ERROR, _L "Out-bound Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); -}*/ - //NOTE: we prolly want to reset counters if we are stupposed to do anything after this. } void EQStream::PacketQueueClear() diff --git a/common/mail_oplist.h b/common/mail_oplist.h index f10e62be0..29cb859e0 100644 --- a/common/mail_oplist.h +++ b/common/mail_oplist.h @@ -1,13 +1,13 @@ //Mail and Chat Channels -N(OP_MailLogin), -N(OP_Mail), +N(OP_Buddy), N(OP_ChannelAnnounceJoin), N(OP_ChannelAnnounceLeave), -N(OP_Buddy), -N(OP_MailHeaderCount), -N(OP_MailHeader), -N(OP_MailSendBody), -N(OP_MailNew), -N(OP_MailDeliveryStatus), -N(OP_MailboxChange), N(OP_Ignore), +N(OP_Mail), +N(OP_MailboxChange), +N(OP_MailDeliveryStatus), +N(OP_MailHeader), +N(OP_MailHeaderCount), +N(OP_MailLogin), +N(OP_MailNew), +N(OP_MailSendBody), diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 352fc62ef..3920dd3f7 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -305,7 +305,7 @@ OUTz(OP_FinishWindow2); //OUTv(OP_AdventureInfo, strlen(p)+1); //OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); OUTv(OP_ItemPacket, ItemPacket_Struct); -OUTv(OP_BuffFadeMsg, BuffFadeMsg_Struct); +OUTv(OP_ColoredText, ColoredText_Struct); OUTv(OP_FormattedMessage, FormattedMessage_Struct); OUTv(OP_GuildMemberList, uint32); //variable length, but nasty OUTv(OP_InterruptCast, InterruptCast_Struct); diff --git a/common/opcode_map.cpp b/common/opcode_map.cpp index 10babaccb..edb6ba64c 100644 --- a/common/opcode_map.cpp +++ b/common/opcode_map.cpp @@ -160,7 +160,7 @@ void load_opcode_names() opcode_map[0x0192]="LiveOP_YellForHelp"; opcode_map[0x00ef]="LiveOP_SafePoint"; opcode_map[0x0157]="LiveOP_Buff"; - opcode_map[0x00c0]="LiveOP_BuffFadeMsg"; + opcode_map[0x00c0]="LiveOP_ColoredText"; opcode_map[0x0440]="LiveOP_MultiLineMsg"; opcode_map[0x021c]="LiveOP_SpecialMesg"; opcode_map[0x0013]="LiveOP_Consent"; diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 27b808620..73726e165 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -2969,7 +2969,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 2d1b5ef18..79b4f21b8 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4106,7 +4106,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 99a904b86..ffd6e9ecb 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3693,7 +3693,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/sof_opcode_list.h b/common/patches/sof_opcode_list.h index 28a1ce650..15236862f 100644 --- a/common/patches/sof_opcode_list.h +++ b/common/patches/sof_opcode_list.h @@ -187,7 +187,7 @@ 0x1ee9, 0x7f5d, OP_CastSpell 0x0659, OP_ManaChange -0x3bc7, OP_BuffFadeMsg +0x3bc7, OP_ColoredText 0x3209, 0x6a93, OP_MemorizeSpell 0x1237, diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index a31d60ef8..7d250a568 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3556,7 +3556,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 1c058fb77..1fcd31b77 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3046,7 +3046,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 860fdd34a..8226e8fd7 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -3735,7 +3735,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/struct_strategy.cpp b/common/struct_strategy.cpp index 9507fd231..9b568b12e 100644 --- a/common/struct_strategy.cpp +++ b/common/struct_strategy.cpp @@ -17,6 +17,11 @@ StructStrategy::StructStrategy() { } void StructStrategy::Encode(EQApplicationPacket **p, EQStream *dest, bool ack_req) const { + if((*p)->GetOpcodeBypass() != 0) { + PassEncoder(p, dest, ack_req); + return; + } + EmuOpcode op = (*p)->GetOpcode(); Encoder proc = encoders[op]; proc(p, dest, ack_req); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f7972e98f..428e8b5b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ SET(tests_sources SET(tests_headers atobool_test.h + data_verification_test.h fixed_memory_test.h fixed_memory_variable_test.h hextoi_32_64_test.h @@ -20,6 +21,8 @@ ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) TARGET_LINK_LIBRARIES(tests common cppunit) +INSTALL(TARGETS tests RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + IF(MSVC) SET_TARGET_PROPERTIES(tests PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") TARGET_LINK_LIBRARIES(tests "Ws2_32.lib") diff --git a/tests/data_verification_test.h b/tests/data_verification_test.h new file mode 100644 index 000000000..1a151c7fa --- /dev/null +++ b/tests/data_verification_test.h @@ -0,0 +1,94 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_DATA_VERIFICATION_H +#define __EQEMU_TESTS_DATA_VERIFICATION_H + +#include "cppunit/cpptest.h" +#include "../common/data_verification.h" + +class DataVerificationTest : public Test::Suite { + typedef void(DataVerificationTest::*TestFunction)(void); +public: + DataVerificationTest() { + TEST_ADD(DataVerificationTest::Clamp); + TEST_ADD(DataVerificationTest::ClampUpper); + TEST_ADD(DataVerificationTest::ClampLower); + } + + ~DataVerificationTest() { + } + + private: + void Clamp() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::Clamp(value_f, 0.0f, 1000.0f); + float vf2 = EQEmu::Clamp(value_f, 0.0f, 250.0f); + float vf3 = EQEmu::Clamp(value_f, 750.0f, 1000.0f); + + int vi1 = EQEmu::Clamp(value_i, 0, 1000); + int vi2 = EQEmu::Clamp(value_i, 0, 250); + int vi3 = EQEmu::Clamp(value_i, 750, 1000); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + TEST_ASSERT_EQUALS(vf3, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + TEST_ASSERT_EQUALS(vi3, 750); + } + + void ClampUpper() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampUpper(value_f, 1000.0f); + float vf2 = EQEmu::ClampUpper(value_f, 250.0f); + + int vi1 = EQEmu::ClampUpper(value_i, 1000); + int vi2 = EQEmu::ClampUpper(value_i, 250); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + } + + void ClampLower() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampLower(value_f, 0.0f); + float vf2 = EQEmu::ClampLower(value_f, 750.0f); + + int vi1 = EQEmu::ClampLower(value_i, 0); + int vi2 = EQEmu::ClampLower(value_i, 750); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 750); + } +}; + +#endif diff --git a/tests/main.cpp b/tests/main.cpp index 285460da4..9d9b658f9 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -27,6 +27,7 @@ #include "atobool_test.h" #include "hextoi_32_64_test.h" #include "string_util_test.h" +#include "data_verification_test.h" int main() { try { @@ -40,6 +41,7 @@ int main() { tests.add(new atoboolTest()); tests.add(new hextoi_32_64_Test()); tests.add(new StringUtilTest()); + tests.add(new DataVerificationTest()); tests.run(*output, true); } catch(...) { return -1; diff --git a/tests/string_util_test.h b/tests/string_util_test.h index 32efb5320..facb0cc72 100644 --- a/tests/string_util_test.h +++ b/tests/string_util_test.h @@ -23,7 +23,7 @@ #include "../common/string_util.h" class StringUtilTest : public Test::Suite { - typedef void(IPCMutexTest::*TestFunction)(void); + typedef void(StringUtilTest::*TestFunction)(void); public: StringUtilTest() { TEST_ADD(StringUtilTest::StringFormatTest); @@ -35,7 +35,7 @@ public: } private: - void StringFormatTest() { + void StringFormatTest() { const char* fmt = "Test: %c %d %4.2f"; char c = 'a'; int i = 2014; diff --git a/utils/patches/opcodes.conf b/utils/patches/opcodes.conf index ed2a685d7..562ba3347 100644 --- a/utils/patches/opcodes.conf +++ b/utils/patches/opcodes.conf @@ -147,7 +147,7 @@ OP_Begging=0x13e7 # ShowEQ 10/27/05 OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_LFGCommand=0x68ac # ShowEQ 10/27/05 OP_LFGGetMatchesRequest=0x022f # ShowEQ 10/27/05 diff --git a/utils/patches/patch_6.2.conf b/utils/patches/patch_6.2.conf index 25944a3b5..8eca3dd4e 100644 --- a/utils/patches/patch_6.2.conf +++ b/utils/patches/patch_6.2.conf @@ -150,7 +150,7 @@ OP_InspectRequest=0x2403 OP_Action2=0x0000 # ShowEQ 06/29/05 OP_BeginCast=0x3990 # ShowEQ 06/29/05 OP_WhoAllRequest=0x5cdd # ShowEQ 06/29/05 -OP_BuffFadeMsg=0x4bc6 # ShowEQ 06/29/05 +OP_ColoredText=0x4bc6 # ShowEQ 06/29/05 OP_Consent=0x1081 # ShowEQ 06/29/05 OP_LFGCommand=0x022f # ShowEQ 06/29/05 OP_LFGGetMatchesRequest=0x6f82 # ShowEQ 06/29/05 diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 50bfd5580..728676b2d 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -166,7 +166,7 @@ OP_InspectRequest=0x23f1 OP_InspectAnswer=0x5794 OP_InspectMessageUpdate=0x3064 OP_BeginCast=0x17ff -OP_BuffFadeMsg=0x41cb +OP_ColoredText=0x41cb OP_ConsentResponse=0x183d OP_MemorizeSpell=0x2fac OP_SwapSpell=0x4736 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 642e0f4ab..10df077f7 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x3563 # C OP_InspectAnswer=0x4938 # C OP_Action2=0x7e4d # C OP_Damage? OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x569a # C +OP_ColoredText=0x569a # C OP_ConsentResponse=0x6e47 # C OP_MemorizeSpell=0x8543 # C OP_SwapSpell=0x3fd2 # C @@ -659,4 +659,4 @@ OP_RAWOutOfSession=0x0000 # # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs -OP_InitialHPUpdate=0x0000 # \ No newline at end of file +OP_InitialHPUpdate=0x0000 # diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 819096ed4..5bffdfc93 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -164,7 +164,7 @@ OP_GMLastName=0x0375 #/lastname OP_InspectAnswer=0x084F #SEQ 12/04/08 OP_Action2=0x0EF2 #SEQ 12/04/08 OP_BeginCast=0x5A50 #SEQ 12/04/08 -OP_BuffFadeMsg=0x3BC7 #SEQ 12/04/08 +OP_ColoredText=0x3BC7 #SEQ 12/04/08 OP_ConsentResponse=0x4D30 #SEQ 12/04/08 OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 OP_SwapSpell=0x1418 #SEQ 12/04/08 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 93f24e8db..50e426874 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -154,7 +154,7 @@ OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_InspectAnswer=0x2403 # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_ConsentDeny=0x4e8c # ShowEQ 10/27/05 OP_ConsentResponse=0x6380 # ShowEQ 10/27/05 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index cce73ce45..4621a74a9 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x7bfb # C OP_InspectAnswer=0x0c2b # C OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x71bf # C +OP_ColoredText=0x71bf # C OP_ConsentResponse=0x0e87 # C OP_MemorizeSpell=0x3887 # C OP_SwapSpell=0x5805 # C diff --git a/zone/client.cpp b/zone/client.cpp index 056f7eeb5..964f228d1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -701,14 +701,10 @@ void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CO } void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) { - - //std::cout << "Sending: 0x" << std::hex << std::setw(4) << std::setfill('0') << (*app)->GetOpcode() << std::dec << ", size=" << (*app)->size << std::endl; - // if the program doesnt care about the status or if the status isnt what we requested if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) { // todo: save packets for later use AddPacket(app, ack_req); -// LogFile->write(EQEMuLog::Normal, "Adding Packet to list (%d) (%d)", (*app)->GetOpcode(), (int)required_state); return; } else { @@ -8317,3 +8313,18 @@ float Client::GetQuiverHaste() quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); return quiver_haste; } + +void Client::SendColoredText(uint32 color, std::string message) +{ + // arbitrary size limit + if (message.size() > 512) // live does send this with empty strings sometimes ... + return; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ColoredText, + sizeof(ColoredText_Struct) + message.size()); + ColoredText_Struct *cts = (ColoredText_Struct *)outapp->pBuffer; + cts->color = color; + strcpy(cts->msg, message.c_str()); + QueuePacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/client.h b/zone/client.h index 0ad8b043a..235861396 100644 --- a/zone/client.h +++ b/zone/client.h @@ -255,6 +255,7 @@ public: const char *message7 = nullptr, const char *message8 = nullptr, const char *message9 = nullptr); void Tell_StringID(uint32 string_id, const char *who, const char *message); + void SendColoredText(uint32 color, std::string message); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a8057f822..62e31c075 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -84,312 +84,313 @@ std::map ConnectingOpcodes; //Use a static array for connected, for speed ClientPacketProc ConnectedOpcodes[_maxEmuOpcode]; -void MapOpcodes() { +void MapOpcodes() +{ ConnectingOpcodes.clear(); memset(ConnectedOpcodes, 0, sizeof(ConnectedOpcodes)); - //Now put all the opcodes into their home... - //Begin Connecting opcodes: - ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; - ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; - ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; - ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; - ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; - ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; - ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; - ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; - ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; - ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; - ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + // Now put all the opcodes into their home... + // connecting opcode handler assignments: ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; - ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; - ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; - ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; - ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; + ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; + ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; // temporary hack + ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; + ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; + ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; + ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; + ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; // I guess it didn't believe us with the first assignment? + ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; + ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; + ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; + ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; + ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; + ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; + ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; ConnectingOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; -//temporary hack: - ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; + ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - //Begin Connected opcodes: - ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; - ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; - ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; - ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; - ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; - ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; - ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; - ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; - ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; - ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; - ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; - ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; - ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; - ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; - ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; - ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; - ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; - ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; - ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; - ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; - ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; - ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; - ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; - ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; - ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; - ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; - ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; - ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; - ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; - ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; - ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; - ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; - ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; - ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; - ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; - ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; - ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; - ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; - ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; - ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; - ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; - ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; - ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; - ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; - ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; - ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; - ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; - ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; - ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; - ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; - ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; - ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; - ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; - ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; - ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; - ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; - ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; - ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; - ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; - ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; - ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; - ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; - ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; - ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; - ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; - ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; - ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; - ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; - ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; - ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; - ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; - ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; - ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; - ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; - ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; - ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; - ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; - ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; - ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; - ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; - ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; - ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; - ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; - ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; - ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; - ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; - ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; - ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; - ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; - ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; - ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; - ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; - ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; - ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; - ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; - ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; - ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; - ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; - ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; - ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; - ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; - ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; - ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; - ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; - ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; - ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; - ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; - ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; - ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; - ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; - ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; - ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; - ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; - ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; - ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; - ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; - ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; - ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; - ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; - ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; - ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; - ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; - ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; - ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; - ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; - ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; - ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; - ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; - ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; - ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; - ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; - ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; - ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; - ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; - ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; - ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; - ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; - ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; - ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; - ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; - ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; - ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; - ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; - ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; - ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; - ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; - ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; - ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; - ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; - ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; - ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; - ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; - ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; - ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; - ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; - ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; - ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; - ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; - ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; - ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; - ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; - ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; - ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; - ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; - ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; - ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; - ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + // connected opcode handler assignments: ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193; - ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; - ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; - ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; - ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; - ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; - ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; - ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; - ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; - ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; - ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; - ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; - ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; - ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; - ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; - ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; - ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; - ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; - ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; - ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; - ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; - ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; - ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; - ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; - ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; - ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; - ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; - ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; - ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; - ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; - ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; - ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; - ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; - ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; - ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask; - ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; - ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; - ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; - ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; - ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; - ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; - ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; - ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; - ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; - ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; - ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; - ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; - ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; - ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; - ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; - ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; - ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; - ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; - ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; - ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; - ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; + ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest; - ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; - ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; - ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; - ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; - ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; - ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; - ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; - ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; - ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; - ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; - ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; - ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; - ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; - ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; - ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; - ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; - ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; - ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; + ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; + ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; + ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; + ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest; - ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase; ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim; ConnectedOpcodes[OP_AltCurrencySell] = &Client::Handle_OP_AltCurrencySell; - ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; - ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; - ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; - ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; - ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; - ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; - ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; - ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; - ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; - ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; - ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; - ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; - ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; - ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; - ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; + ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; + ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; + ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; + ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; + ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; + ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; + ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; + ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; + ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; + ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; + ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; + ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; + ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; + ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; + ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; + ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; + ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; + ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; + ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; + ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; + ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; + ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; + ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; + ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; + ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; + ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; + ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; + ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; + ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; + ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; + ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; + ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; + ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; ConnectedOpcodes[OP_ClientTimeStamp] = &Client::Handle_OP_ClientTimeStamp; + ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; + ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; + ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; + ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; + ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; + ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; + ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; + ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; + ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; + ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; + ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; + ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; + ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; + ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; + ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; + ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; + ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; + ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; + ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; + ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; + ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; + ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; + ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; + ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; + ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; + ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; + ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; + ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; + ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; + ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; + ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; + ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; + ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; + ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; + ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; + ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; + ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; + ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; + ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; + ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; + ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; + ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; + ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; + ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; + ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; + ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; + ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; + ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; + ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; + ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; + ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; + ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; + ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; + ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; + ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; + ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; + ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; + ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; + ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; + ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; + ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; + ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; + ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; + ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; + ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; + ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; + ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; + ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; + ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; + ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; + ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; + ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; + ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; + ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; + ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; + ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; + ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; + ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; + ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; + ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; + ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; + ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; + ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; + ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; + ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; + ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; + ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; + ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; + ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; + ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; + ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; + ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; + ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; + ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; + ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; + ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; + ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; + ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; + ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; + ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; + ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; + ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; + ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; + ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; + ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; + ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; + ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; + ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; + ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; + ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; + ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; + ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; + ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; + ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; + ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; + ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; + ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; + ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; + ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; + ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; + ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; + ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; + ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; + ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; + ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; + ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; + ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; + ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; + ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; + ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; + ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; + ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; + ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; + ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; + ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; + ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; + ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; + ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; + ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; + ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; + ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; + ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; + ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; + ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; + ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; + ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; + ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; + ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; + ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; + ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; + ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; + ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; + ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; + ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; + ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; + ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; + ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; + ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; + ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; + ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; + ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; + ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; + ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; + ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; + ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; + ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; + ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; + ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; + ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; + ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; + ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; + ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; + ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; + ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; + ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; + ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; + ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; + ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; + ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; + ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; + ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; + ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; + ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; + ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; + ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; + ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; + ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; + ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; + ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; + ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; + ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; + ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; + ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; + ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; + ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; + ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; + ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; + ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; + ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; + ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; + ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; + ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; + ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; + ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; + ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; + ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; + ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; + ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; + ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; + ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; + ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; } -void ClearMappedOpcode(EmuOpcode op) { +void ClearMappedOpcode(EmuOpcode op) +{ if(op >= _maxEmuOpcode) return; @@ -400,6 +401,7 @@ void ClearMappedOpcode(EmuOpcode op) { } } +// client methods int Client::HandlePacket(const EQApplicationPacket *app) { if(is_log_enabled(CLIENT__NET_IN_TRACE)) { @@ -495,7 +497,805 @@ int Client::HandlePacket(const EQApplicationPacket *app) return(true); } -void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { +// Finish client connecting state +void Client::CompleteConnect() +{ + UpdateWho(); + client_state = CLIENT_CONNECTED; + + hpupdate_timer.Start(); + position_timer.Start(); + autosave_timer.Start(); + SetDuelTarget(0); + SetDueling(false); + + EnteringMessages(this); + LoadZoneFlags(); + + /* Sets GM Flag if needed & Sends Petition Queue */ + UpdateAdmin(false); + + if (IsInAGuild()){ + uint8 rank = GuildRank(); + if (GetClientVersion() >= EQClientRoF) + { + switch (rank) { + case 0: { rank = 5; break; } // GUILD_MEMBER 0 + case 1: { rank = 3; break; } // GUILD_OFFICER 1 + case 2: { rank = 1; break; } // GUILD_LEADER 2 + default: { break; } // GUILD_NONE + } + } + SendAppearancePacket(AT_GuildID, GuildID(), false); + SendAppearancePacket(AT_GuildRank, rank, false); + } + for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { + if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) + m_pp.spell_book[spellInt] = 0xFFFFFFFF; + } + //SendAATable(); + + if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); + + uint32 raidid = database.GetRaidID(GetName()); + Raid *raid = nullptr; + if (raidid > 0){ + raid = entity_list.GetRaidByID(raidid); + if (!raid){ + raid = new Raid(raidid); + if (raid->GetID() != 0){ + entity_list.AddRaid(raid, raidid); + } + else + raid = nullptr; + } + if (raid){ + SetRaidGrouped(true); + raid->LearnMembers(); + raid->VerifyRaid(); + raid->GetRaidDetails(); + /* + Only leader should get this; send to all for now till + I figure out correct creation; can probably also send a no longer leader packet for non leaders + but not important for now. + */ + raid->SendRaidCreate(this); + raid->SendMakeLeaderPacketTo(raid->leadername, this); + raid->SendRaidAdd(GetName(), this); + raid->SendBulkRaid(this); + raid->SendGroupUpdate(this); + uint32 grpID = raid->GetGroup(GetName()); + if (grpID < 12){ + raid->SendRaidGroupRemove(GetName(), grpID); + raid->SendRaidGroupAdd(GetName(), grpID); + } + if (raid->IsLocked()) + raid->SendRaidLockTo(this); + } + } + + //bulk raid send in here eventually + + //reapply some buffs + uint32 buff_count = GetMaxTotalSlots(); + for (uint32 j1 = 0; j1 < buff_count; j1++) { + if (!IsValidSpell(buffs[j1].spellid)) + continue; + + const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; + + int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); + if (NimbusEffect) { + if (!IsNimbusEffectActive(NimbusEffect)) + SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); + } + + for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { + switch (spell.effectid[x1]) { + case SE_IllusionCopy: + case SE_Illusion: { + if (spell.base[x1] == -1) { + if (gender == 1) + gender = 0; + else if (gender == 0) + gender = 1; + SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); + } + else if (spell.base[x1] == -2) + { + if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) + SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); + } + else if (spell.max[x1] > 0) + { + SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); + } + else + { + SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + } + switch (spell.base[x1]){ + case OGRE: + SendAppearancePacket(AT_Size, 9); + break; + case TROLL: + SendAppearancePacket(AT_Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AT_Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AT_Size, 5); + break; + case DWARF: + SendAppearancePacket(AT_Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AT_Size, 3); + break; + default: + SendAppearancePacket(AT_Size, 6); + break; + } + break; + } + case SE_SummonHorse: { + SummonHorse(buffs[j1].spellid); + //hasmount = true; //this was false, is that the correct thing? + break; + } + case SE_Silence: + { + Silence(true); + break; + } + case SE_Amnesia: + { + Amnesia(true); + break; + } + case SE_DivineAura: + { + invulnerable = true; + break; + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AT_Invis, 1); + break; + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + if (!GetGM()) + { + SendAppearancePacket(AT_Levitate, 0); + BuffFadeByEffect(SE_Levitate); + Message(13, "You can't levitate in this zone."); + } + } + else{ + SendAppearancePacket(AT_Levitate, 2); + } + break; + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; + break; + } + case SE_InvisVsAnimals: + { + invisible_animals = true; + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + } + } + } + + /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ + entity_list.SendZoneAppearance(this); + /* Sends the Nimbus particle effects (up to 3) for any mob using them */ + entity_list.SendNimbusEffects(this); + + entity_list.SendUntargetable(this); + + int x; + for (x = 0; x < 8; x++) + SendWearChange(x); + Mob *pet = GetPet(); + if (pet != nullptr) { + for (x = 0; x < 8; x++) + pet->SendWearChange(x); + } + + entity_list.SendTraders(this); + + zoneinpacket_timer.Start(); + + if (GetPet()){ + GetPet()->SendPetBuffsToClient(); + } + + if (GetGroup()) + database.RefreshGroupFromDB(this); + + if (RuleB(TaskSystem, EnableTaskSystem)) + TaskPeriodic_Timer.Start(); + else + TaskPeriodic_Timer.Disable(); + + conn_state = ClientConnectFinished; + + //enforce some rules.. + if (!CanBeInZone()) { + _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); + GoToSafeCoords(database.GetZoneID("arena"), 0); + return; + } + + if (zone) + zone->weatherSend(); + + TotalKarma = database.GetKarma(AccountID()); + SendDisciplineTimers(); + + parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); + + /* This sub event is for if a player logs in for the first time since entering world. */ + if (firstlogon == 1){ + parse->EventPlayer(EVENT_CONNECT, this, "", 0); + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } + } + + if (zone) { + if (zone->GetInstanceTimer()) { + uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); + uint32 day = (ttime / 86400000); + uint32 hour = (ttime / 3600000) % 24; + uint32 minute = (ttime / 60000) % 60; + uint32 second = (ttime / 1000) % 60; + if (day) { + Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); + } + else if (hour) { + Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); + } + else if (minute) { + Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), minute, second); + } + else { + Message(15, "%s(%u) will expire in in %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), second); + } + } + } + + SendRewards(); + SendAltCurrencies(); + database.LoadAltCurrencyValues(CharacterID(), alternate_currency); + SendAlternateCurrencyValues(); + alternate_currency_loaded = true; + ProcessAlternateCurrencyQueue(); + + /* This needs to be set, this determines whether or not data was loaded properly before a save */ + client_data_loaded = true; + + CalcItemScale(); + DoItemEnterZone(); + + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); + + if (GetClientVersion() >= EQClientSoD) + entity_list.SendFindableNPCList(this); + + if (IsInAGuild()) { + SendGuildRanks(); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); + guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); + } + + /** Request adventure info **/ + ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); + strcpy((char*)pack->pBuffer, GetName()); + worldserver.SendPacket(pack); + delete pack; + + if (IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + + entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); +} + +void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) +{ + //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. + + switch (CheatType) + { + case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + Message(13, "Large warp detected."); + char hString[250]; + sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQWarpShadowStep: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + case MQWarpKnockBack: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + + case MQWarpLight: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + if (RuleB(Zone, MarkMQWarpLT)) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + } + break; + + case MQZone: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQZoneUnknownDest: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQGate: + if (RuleB(Zone, EnableMQGateDetector) && ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { + Message(13, "Illegal gate request."); + char hString[250]; + sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + if (zone) + { + this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + } + else + { + this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + + } + } + break; + case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified + if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { + database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + break; + default: + char *hString = nullptr; + MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + break; + } +} + +// connecting opcode handlers +/* +void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) +{ +//OP_0x0380 = 0x642c +EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno +QueuePacket(outapp); +safe_delete(outapp); +return; +} +*/ + +void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ApproveZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", + sizeof(ApproveZone_Struct), app->size); + return; + } + ApproveZone_Struct* azone = (ApproveZone_Struct*)app->pBuffer; + azone->approve = 1; + QueuePacket(app); + return; +} + +void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientError_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", + sizeof(ClientError_Struct), app->size); + return; + } + // Client reporting error to server + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); + Message(13, error->message); +#if (EQDEBUG>=5) + DumpPacket(app); +#endif + return; +} + +void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) +{ + conn_state = ClientReadyReceived; + + CompleteConnect(); + SendHPUpdate(); +} + +void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) +{ + //Once we get this, the client thinks it is connected + //So give it the benefit of the doubt and move to connected + + Handle_Connect_OP_ClientReady(app); +} + +void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) +{ + conn_state = ClientSpawnRequested; + + EQApplicationPacket* outapp = new EQApplicationPacket; + + // Send Zone Doors + if (entity_list.MakeDoorSpawnPacket(outapp, this)) + { + QueuePacket(outapp); + } + safe_delete(outapp); + + // Send Zone Objects + entity_list.SendZoneObjects(this); + SendZonePoints(); + // Live does this + outapp = new EQApplicationPacket(OP_SendAAStats, 0); + FastQueuePacket(&outapp); + + // Tell client they can continue we're done + outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); + FastQueuePacket(&outapp); + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + FastQueuePacket(&outapp); + + if (GetClientVersion() >= EQClientRoF) + { + outapp = new EQApplicationPacket(OP_ClientReady, 0); + FastQueuePacket(&outapp); + } + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + if (strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) + SendBazaarWelcome(); + + conn_state = ZoneContentsSent; + + return; +} + +void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) +{ + conn_state = NewZoneRequested; + + EQApplicationPacket* outapp; + + ///////////////////////////////////// + // New Zone Packet + outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; + memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); + strcpy(nz->char_name, m_pp.name); + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) +{ + SendAATimers(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +{ + SendAAList(); + return; +} + +void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) +{ + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + QueuePacket(outapp); + safe_delete(outapp); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + /* this is actually the guild MOTD + outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); + ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; + strcpy(zonesendname2->name,m_pp.name); + QueuePacket(outapp); + safe_delete(outapp);*/ + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) +{ + SendGuildTributes(); + return; +} + +void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) +{ + SendTributes(); + return; +} + +void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", + sizeof(uint32), app->size); + return; + } + OPTGB(app); + return; +} + +void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) +{ + SendAATable(); +} + +void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) +{ + //not sure what these are supposed to mean to us. + return; +} + +void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) +{ + //This is a copy of SendExpZonein created for SoF due to packet order change + //This does not affect clients other than SoF + + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) +{ if(app->size != sizeof(ClientZoneEntry_Struct)) return; ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; @@ -597,6 +1397,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ + database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) @@ -1028,448 +1829,2481 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { return; } -void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +// connected opcode handlers +void Client::Handle_0x0193(const EQApplicationPacket *app) { - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); + // Not sure what this opcode does. It started being sent when OP_ClientUpdate was + // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate + // 2 bytes: 00 00 + + return; +} + +void Client::Handle_OP_AAAction(const EQApplicationPacket *app) +{ + mlog(AA__IN, "Received OP_AAAction"); + mpkt(AA__IN, app); + + if (app->size != sizeof(AA_Action)){ + printf("Error! OP_AAAction size didnt match!\n"); + return; + } + AA_Action* action = (AA_Action*)app->pBuffer; + + if (action->action == aaActionActivate) {//AA Hotkey + mlog(AA__MESSAGE, "Activating AA %d", action->ability); + ActivateAA((aaID)action->ability); + } + else if (action->action == aaActionBuy) { + BuyAA(action); + } + else if (action->action == aaActionDisableEXP){ //Turn Off AA Exp + if (m_epp.perAA > 0) + Message_StringID(0, AA_OFF); + m_epp.perAA = 0; + SendAAStats(); + } + else if (action->action == aaActionSetEXP) { + if (m_epp.perAA == 0) + Message_StringID(0, AA_ON); + m_epp.perAA = action->exp_value; + if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA = 0; // stop exploit with sanity check + // send an update + SendAAStats(); + SendAATable(); + } + else { + printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); + } + + return; +} + +void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(AcceptNewTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", + sizeof(AcceptNewTask_Struct), app->size); DumpPacket(app); return; } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; + AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; + + if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); } -void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) { - SendAAList(); - return; -} - -void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) -{ - SendTributes(); - return; -} - -void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) -{ - SendGuildTributes(); - return; -} - -void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) -{ - SendAATimers(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -//void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) -//{ -/*OP_0x0380 = 0x642c*/ - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno - //QueuePacket(outapp); - //safe_delete(outapp); - //return; -//} - -void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) -{ - conn_state = ClientSpawnRequested; - - EQApplicationPacket* outapp = new EQApplicationPacket; - - // Send Zone Doors - if(entity_list.MakeDoorSpawnPacket(outapp, this)) + if (app->size < sizeof(EntityId_Struct)) { - QueuePacket(outapp); + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); + return; } - safe_delete(outapp); - - // Send Zone Objects - entity_list.SendZoneObjects(this); - SendZonePoints(); - // Live does this - outapp = new EQApplicationPacket(OP_SendAAStats, 0); - FastQueuePacket(&outapp); - - // Tell client they can continue we're done - outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); - FastQueuePacket(&outapp); - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - FastQueuePacket(&outapp); - - if(GetClientVersion() >= EQClientRoF) + EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; + Mob * m = entity_list.GetMob(ent->entity_id); + if (m && m->IsNPC()) { - outapp = new EQApplicationPacket(OP_ClientReady, 0); + std::map::iterator it; + it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); + if (it != zone->adventure_entry_list_flavor.end()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); + strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); + FastQueuePacket(&outapp); + } + else + { + if (m->CastToNPC()->GetAdventureTemplate() != 0) + { + std::string text = "Choose your difficulty and preferred adventure type."; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); + strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); + FastQueuePacket(&outapp); + } + } + } +} + +void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureLeaderboardRequest_Struct)) + { + return; + } + + if (adventure_leaderboard_timer) + { + return; + } + + adventure_leaderboard_timer = new Timer(4000); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); + ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; + strcpy(lr->player, GetName()); + + AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; + lr->type = 1 + (lrs->theme * 2) + lrs->type; + worldserver.SendPacket(pack); + delete pack; +} + +void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Purchase_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); + return; + } + + Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; + /* + Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) + if(ldon_points_available >= item ldonpointcost) + { + give item (67 00 00 00 for the packettype using opcode 0x02c5) + ldon_points_available -= ldonpointcost; + } + */ + uint32 merchantid = 0; + Mob* tmp = entity_list.GetMob(aps->npcid); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + const Item_Struct* item = nullptr; + bool found = false; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + if (item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + found = true; + break; + } + } + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (aps->Type == LDoNMerchant) + { + if (m_pp.ldon_points_available < int32(item->LDoNPrice)) { + Message(13, "You cannot afford that item."); + return; + } + + if (item->LDoNTheme <= 16) + { + if (item->LDoNTheme & 16) + { + if (m_pp.ldon_points_tak < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 8) + { + if (m_pp.ldon_points_ruj < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 4) + { + if (m_pp.ldon_points_mmc < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 2) + { + if (m_pp.ldon_points_mir < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 1) + { + if (m_pp.ldon_points_guk < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + } + } + else if (aps->Type == DiscordMerchant) + { + if (GetPVPPoints() < item->LDoNPrice) + { + Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == NorrathsKeepersMerchant) + { + if (GetRadiantCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == DarkReignMerchant) + { + if (GetEbonCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else + { + Message(13, "Unknown Adventure Merchant type."); + return; + } + + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + if (aps->Type == LDoNMerchant) + { + int32 requiredpts = (int32)item->LDoNPrice*-1; + + if (!UpdateLDoNPoints(requiredpts, 6)) + return; + } + else if (aps->Type == DiscordMerchant) + { + SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); + SendPVPStats(); + } + else if (aps->Type == NorrathsKeepersMerchant) + { + SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + else if (aps->Type == DarkReignMerchant) + { + SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + Save(1); +} + +void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AdventureMerchant_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); + return; + } + std::stringstream ss(std::stringstream::in | std::stringstream::out); + + uint8 count = 0; + AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; + uint32 merchantid = 0; + + Mob* tmp = entity_list.GetMob(eid->entity_id); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + tmp->CastToNPC()->FaceTarget(this->CastToMob()); + + const Item_Struct *item = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count<255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + uint32 theme; + if (item->LDoNTheme > 16) + { + theme = 0; + } + else if (item->LDoNTheme & 16) + { + theme = 5; + } + else if (item->LDoNTheme & 8) + { + theme = 4; + } + else if (item->LDoNTheme & 4) + { + theme = 3; + } + else if (item->LDoNTheme & 2) + { + theme = 2; + } + else if (item->LDoNTheme & 1) + { + theme = 1; + } + else + { + theme = 0; + } + ss << "^" << item->Name << "|"; + ss << item->ID << "|"; + ss << item->LDoNPrice << "|"; + ss << theme << "|"; + ss << "0|"; + ss << "1|"; + ss << item->Races << "|"; + ss << item->Classes; + count++; + } + } + //Count + //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse, ss.str().size() + 2); + outapp->pBuffer[0] = count; + strn0cpy((char*)&outapp->pBuffer[1], ss.str().c_str(), ss.str().size()); + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Sell_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", + app->size, sizeof(Adventure_Sell_Struct)); + DumpPacket(app); + return; + } + + Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(ams_in->npcid); + if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && + (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) + { + Message(13, "Vendor was not found."); + return; + } + + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + { + Message(13, "Vendor is out of range."); + return; + } + + uint32 itemid = GetItemIDAt(ams_in->slot); + + if (itemid == 0) + { + Message(13, "Found no item at that slot."); + return; + } + + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(ams_in->slot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item..."); + return; + } + + // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor + // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the + // items. + // + // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, + // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting + // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for + // it, but he will refuse for item 76053. + // + // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. + // + // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle + // that case here. + if (item->LDoNSold == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (item->LDoNPrice == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + int32 price = item->LDoNPrice * 70 / 100; + + if (price == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, ams_in->charges, price, item, false); + + if (!inst->IsStackable()) + { + DeleteItemInInventory(ams_in->slot, 0, false); + } + else + { + if (inst->GetCharges() < ams_in->charges) + { + ams_in->charges = inst->GetCharges(); + } + + if (ams_in->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(ams_in->slot, ams_in->charges, false); + price *= ams_in->charges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); + Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; + ams->slot = ams_in->slot; + ams->unknown000 = 1; + ams->npcid = ams->npcid; + ams->charges = ams_in->charges; + ams->sell_price = price; + FastQueuePacket(&outapp); + + switch (vendor->GetClass()) + { + case ADVENTUREMERCHANT: + { + UpdateLDoNPoints(price, 6); + break; + } + case NORRATHS_KEEPERS_MERCHANT: + { + SetRadiantCrystals(GetRadiantCrystals() + price); + break; + } + case DARK_REIGN_MERCHANT: + { + SetEbonCrystals(GetEbonCrystals() + price); + break; + } + + default: + break; + } + + Save(1); +} + +void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); + return; + } + + if (IsOnAdventure()) + { + return; + } + + if (!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) + { + return; + } + + if (GetPendingAdventureRequest()) + { + return; + } + + AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; + uint8 group_members = 0; + Raid *r = nullptr; + Group *g = nullptr; + + if (IsRaidGrouped()) + { + r = GetRaid(); + group_members = r->RaidCount(); + } + else if (IsGrouped()) + { + g = GetGroup(); + group_members = g->GroupCount(); + } + else + { + return; + } + + if (group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) + { + return; + } + + Mob* m = entity_list.GetMob(ars->entity_id); + uint32 template_id = 0; + if (m && m->IsNPC()) + { + template_id = m->CastToNPC()->GetAdventureTemplate(); + } + else + { + return; + } + + ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct)+(64 * group_members)); + ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; + sar->member_count = group_members; + sar->risk = ars->risk; + sar->type = ars->type; + sar->template_id = template_id; + strcpy(sar->leader, GetName()); + + if (IsRaidGrouped()) + { + int i = 0; + for (int x = 0; x < 72; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = r->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + else + { + int i = 0; + for (int x = 0; x < 6; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = g->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + + packet->Deflate(); + worldserver.SendPacket(packet); + delete packet; + p_timers.Start(pTimerStartAdventureTimer, 5); +} + +void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) +{ + if (adventure_stats_timer) + { + return; + } + + adventure_stats_timer = new Timer(8000); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); + AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; + + if (database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, + as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) + { + as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; + as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; + m_pp.ldon_wins_guk = as->success.guk; + m_pp.ldon_wins_mir = as->success.mir; + m_pp.ldon_wins_mmc = as->success.mmc; + m_pp.ldon_wins_ruj = as->success.ruj; + m_pp.ldon_wins_tak = as->success.tak; + m_pp.ldon_losses_guk = as->failure.guk; + m_pp.ldon_losses_mir = as->failure.mir; + m_pp.ldon_losses_mmc = as->failure.mmc; + m_pp.ldon_losses_ruj = as->failure.ruj; + m_pp.ldon_losses_tak = as->failure.tak; + } + + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); + + NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); + bool found = false; + while (altc_iter != zone->AlternateCurrencies.end()) { + if ((*altc_iter).id == alt_cur_id) { + found = true; + break; + } + ++altc_iter; + } + + if (!found) { + return; + } + + std::stringstream ss(std::stringstream::in | std::stringstream::out); + std::stringstream item_ss(std::stringstream::in | std::stringstream::out); + ss << alt_cur_id << "|1|" << alt_cur_id; + uint32 count = 0; + uint32 merchant_id = tar->MerchantType; + const Item_Struct *item = nullptr; + + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + item_ss << "^" << item->Name << "|"; + item_ss << item->ID << "|"; + item_ss << ml.alt_currency_cost << "|"; + item_ss << "0|"; + item_ss << "1|"; + item_ss << item->Races << "|"; + item_ss << item->Classes; + count++; + } + } + + if (count > 0) { + ss << "|" << count << item_ss.str(); + } + else { + ss << "|0"; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); + memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); FastQueuePacket(&outapp); } +} - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); +void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); + AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; - if(strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) - SendBazaarWelcome(); + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } - conn_state = ZoneContentsSent; + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (cost > current_currency) { + Message(13, "You cannot afford that item right now."); + return; + } + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + + Save(1); + } +} + +void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); + AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; + uint32 item_id = 0; + std::list::iterator iter = zone->AlternateCurrencies.begin(); + while (iter != zone->AlternateCurrencies.end()) { + if ((*iter).id == reclaim->currency_id) { + item_id = (*iter).item_id; + } + ++iter; + } + + if (item_id == 0) { + return; + } + + /* Item to Currency Storage */ + if (reclaim->reclaim_flag == 1) { + uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); + if (removed > 0) { + AddAlternateCurrencyValue(reclaim->currency_id, removed); + + /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } + } + /* Cursor to Item storage */ + else { + uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); + + /* If you input more than you have currency wise, just give the max of the currency you currently have */ + if (reclaim->count > max_currency) { + SummonItem(item_id, max_currency); + SetAlternateCurrencyValue(reclaim->currency_id, 0); + } + else { + SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); + AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); + } + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } +} + +void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); + EQApplicationPacket *outapp = app->Copy(); + AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; + + NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst* inst = GetInv().GetItem(sell->slot_id); + if (!inst) { + return; + } + + if (!RuleB(Merchant, EnableAltCurrencySell)) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + uint32 npc_id = tar->GetNPCTypeID(); + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + return; + } + + if (!inst->IsStackable()) + { + DeleteItemInInventory(sell->slot_id, 0, false); + } + else + { + if (inst->GetCharges() < sell->charges) + { + sell->charges = inst->GetCharges(); + } + + if (sell->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(sell->slot_id, sell->charges, false); + cost *= sell->charges; + } + + sell->cost = cost; + + /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + FastQueuePacket(&outapp); + AddAlternateCurrencyValue(alt_cur_id, cost); + Save(1); + } +} + +void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); + + AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst *inst = m_inv.GetItem(select->slot_id); + if (!inst) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + + if (RuleB(Merchant, EnableAltCurrencySell)) { + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + cost = 0; + } + } + else { + cost = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); + AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; + reply->unknown004 = 0xFF; + reply->unknown005 = 0xFF; + reply->unknown006 = 0xFF; + reply->unknown007 = 0xFF; + strcpy(reply->item_name, inst->GetItem()->Name); + reply->cost = cost; + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Animation(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Animation_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Animation: got %d, expected %d", app->size, + sizeof(Animation_Struct)); + DumpPacket(app); + return; + } + + Animation_Struct *s = (Animation_Struct *)app->pBuffer; + + //might verify spawn ID, but it wouldent affect anything + + DoAnim(s->action, s->value); return; } -void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) +void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { - conn_state = NewZoneRequested; + if (app->size != sizeof(ApplyPoison_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); + DumpPacket(app); + return; + } + uint32 ApplyPoisonSuccessResult = 0; + ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; + const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); + const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); + const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - EQApplicationPacket* outapp; + bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - ///////////////////////////////////// - // New Zone Packet - outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; - memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); - strcpy(nz->char_name, m_pp.name); + if (!IsPoison) + { + mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " + "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); + + Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); + } + else if (GetClass() == ROGUE) + { + if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || + (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { + float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; + double ChanceRoll = MakeRandomFloat(0, 1); + + CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); + + if (ChanceRoll < SuccessChance) { + ApplyPoisonSuccessResult = 1; + // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. + // My thinking was that DEX should be apart of the calculation. + AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); + } + + DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); + + LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); + ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; + ApplyPoisonResult->success = ApplyPoisonSuccessResult; + ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; FastQueuePacket(&outapp); - - return; } -void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) +void Client::Handle_OP_Assist(const EQApplicationPacket *app) { - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if(GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - /* this is actually the guild MOTD - outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); - ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; - strcpy(zonesendname2->name,m_pp.name); - QueuePacket(outapp); - safe_delete(outapp);*/ - - if(IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) -{ - //This is a copy of SendExpZonein created for SoF due to packet order change - //This does not affect clients other than SoF - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if(GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if(IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) -{ - //not sure what these are supposed to mean to us. - return; -} - -void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) -{ - //Once we get this, the client thinks it is connected - //So give it the benefit of the doubt and move to connected - - Handle_Connect_OP_ClientReady(app); -} - -void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) -{ - conn_state = ClientReadyReceived; - - CompleteConnect(); - SendHPUpdate(); -} - -void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientError_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", - sizeof(ClientError_Struct), app->size); + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); return; } - // Client reporting error to server - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); - Message(13, error->message); -#if (EQDEBUG>=5) - DumpPacket(app); -#endif + + EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(eid->entity_id); + + EQApplicationPacket* outapp = app->Copy(); + eid = (EntityId_Struct*)outapp->pBuffer; + if (RuleB(Combat, AssistNoTargetSelf)) + eid->entity_id = GetID(); + if (entity && entity->IsMob()) { + Mob *assistee = entity->CastToMob(); + if (assistee->GetTarget()) { + Mob *new_target = assistee->GetTarget(); + if (new_target && (GetGM() || + Dist(*assistee) <= TARGETING_RANGE)) { + SetAssistExemption(true); + eid->entity_id = new_target->GetID(); + } + } + } + + FastQueuePacket(&outapp); return; } -void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) { - if (app->size != sizeof(ApproveZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", - sizeof(ApproveZone_Struct), app->size); + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); return; } - ApproveZone_Struct* azone =(ApproveZone_Struct*)app->pBuffer; - azone->approve=1; QueuePacket(app); return; } -void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - if (app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", - sizeof(uint32), app->size); + + // This packet is sent by the client when an Augment item information window is opened. + // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. + // The OP_Augment packet includes a window parameter to determine which Item window in the UI the + // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. + // + + if (app->size != sizeof(AugmentInfo_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", + sizeof(AugmentInfo_Struct), app->size); + + DumpPacket(app); + return; } - OPTGB(app); - return; -} + AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*)app->pBuffer; -void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) { - SendAATable(); -} + char *outstring = nullptr; -void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) -{ //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. - switch (CheatType) + const Item_Struct * item = database.GetItem(AugInfo->itemid); + + if (item) { - case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - Message(13, "Large warp detected."); - char hString[250]; - sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQWarpShadowStep: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; - case MQWarpKnockBack: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; + MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - case MQWarpLight: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - if(RuleB(Zone, MarkMQWarpLT)) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - } - break; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - case MQZone: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + BookText_Struct *out = (BookText_Struct *)outapp->pBuffer; + + out->window = AugInfo->window; + + out->type = 2; + + out->invslot = 0; + + strcpy(out->booktext, outstring); + + safe_delete_array(outstring); + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AugmentItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", + sizeof(AugmentItem_Struct), app->size); + return; + } + + // Delegate to tradeskill object to perform combine + AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; + bool deleteItems = false; + if (GetClientVersion() >= EQClientRoF) + { + ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; + + //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); + + // Adding augment + if (in_augment->augment_action == 0) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; + //Message(13, "%i AugSlot", aug_slot_id); + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + Message(13, "Error: Invalid Aug Index."); + return; } - break; - case MQZoneUnknownDest: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(MainCursor); + + if (tobe_auged && auged_with) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQGate: - if (RuleB(Zone, EnableMQGateDetector)&& ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { - Message(13, "Illegal gate request."); - char hString[250]; - sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - if(zone) + if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && + (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { - this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + tobe_auged->PutAugment(in_augment->augment_index, *auged_with); + + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); + return; + } + + itemOneToPush = tobe_auged->Clone(); + // Must push items after the items in inventory are deleted - necessary due to lore items... + if (itemOneToPush) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(MainCursor, 0, true); + if (PutItemInInventory(slot_id, *itemOneToPush, true)) + { + //Message(13, "Sucessfully added an augment to your item!"); + return; + } + else + { + Message(13, "Error: No available slot for end result. Please free up some bag space."); + } + } + else + { + Message(13, "Error in cloning item for augment. Aborted."); + } + } else { - this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. - + Message(13, "Error: No available slot for augment in that item."); } } - break; - case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified - if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { - database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + else if (in_augment->augment_action == 1) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) + { + Message(13, "Error: Invalid Aug Index."); + return; } - break; - default: - char *hString = nullptr; - MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - break; + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(aug_slot_id); + + ItemInst *old_aug = nullptr; + if (!auged_with) + return; + const uint32 id = auged_with->GetID(); + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + + args.push_back(false); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting."); + return; + } + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + itemTwoToPush = old_aug->Clone(); + if (itemOneToPush && itemTwoToPush && auged_with) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); + if (!PutItemInInventory(slot_id, *itemOneToPush, true)) + { + Message(15, "Shouldn't happen, contact an admin!"); + } + + if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + //Message(15, "Successfully removed an augmentation!"); + } + } + } } + else + { + Object::HandleAugmentation(this, in_augment, m_tradeskill_object); + } + return; +} + +void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +{ + if (app->size != 4) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); + return; + } + + if (app->pBuffer[0] == 0) + { + auto_attack = false; + if (IsAIControlled()) + return; + attack_timer.Disable(); + ranged_timer.Disable(); + attack_dw_timer.Disable(); + + aa_los_me.x = 0; + aa_los_me.y = 0; + aa_los_me.z = 0; + aa_los_me_heading = 0; + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + } + else if (app->pBuffer[0] == 1) + { + auto_attack = true; + auto_fire = false; + if (IsAIControlled()) + return; + SetAttackTimer(); + + if (GetTarget()) + { + aa_los_them_mob = GetTarget(); + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = aa_los_them_mob->GetX(); + aa_los_them.y = aa_los_them_mob->GetY(); + aa_los_them.z = aa_los_them_mob->GetZ(); + los_status = CheckLosFN(aa_los_them_mob); + los_status_facing = IsFacingMob(aa_los_them_mob); + } + else + { + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + los_status = false; + los_status_facing = false; + } + } +} + +void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) +{ + if (app->size != sizeof(bool)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); + DumpPacket(app); + return; + } + bool *af = (bool*)app->pBuffer; + auto_fire = *af; + auto_attack = false; + SetAttackTimer(); +} + +void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) +{ + + // Although there are three different structs for OP_Bandolier, they are all the same size. + // + if (app->size != sizeof(BandolierCreate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", + sizeof(BandolierCreate_Struct), app->size); + DumpPacket(app); + return; + } + + BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; + + switch (bs->action) { + case BandolierCreate: + CreateBandolier(app); + break; + case BandolierRemove: + RemoveBandolier(app); + break; + case BandolierSet: + SetBandolier(app); + break; + default: + LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); + + } +} + +void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BankerChange_Struct) && app->size != 4) //Titanium only sends 4 Bytes for this + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); + DumpPacket(app); + return; + } + + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + + if (!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", + banker ? banker->GetName() : "UNKNOWN NPC", distance); + database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BankerChange, nullptr, sizeof(BankerChange_Struct)); + BankerChange_Struct *bc = (BankerChange_Struct *)outapp->pBuffer; + + if (m_pp.platinum < 0) + m_pp.platinum = 0; + if (m_pp.gold < 0) + m_pp.gold = 0; + if (m_pp.silver < 0) + m_pp.silver = 0; + if (m_pp.copper < 0) + m_pp.copper = 0; + + if (m_pp.platinum_bank < 0) + m_pp.platinum_bank = 0; + if (m_pp.gold_bank < 0) + m_pp.gold_bank = 0; + if (m_pp.silver_bank < 0) + m_pp.silver_bank = 0; + if (m_pp.copper_bank < 0) + m_pp.copper_bank = 0; + + uint64 cp = static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000); + + m_pp.copper = cp % 10; + cp /= 10; + m_pp.silver = cp % 10; + cp /= 10; + m_pp.gold = cp % 10; + cp /= 10; + m_pp.platinum = cp; + + cp = static_cast(m_pp.copper_bank) + + (static_cast(m_pp.silver_bank) * 10) + + (static_cast(m_pp.gold_bank) * 100) + + (static_cast(m_pp.platinum_bank) * 1000); + + m_pp.copper_bank = cp % 10; + cp /= 10; + m_pp.silver_bank = cp % 10; + cp /= 10; + m_pp.gold_bank = cp % 10; + cp /= 10; + m_pp.platinum_bank = cp; + + bc->copper = m_pp.copper; + bc->silver = m_pp.silver; + bc->gold = m_pp.gold; + bc->platinum = m_pp.platinum; + + bc->copper_bank = m_pp.copper_bank; + bc->silver_bank = m_pp.silver_bank; + bc->gold_bank = m_pp.gold_bank; + bc->platinum_bank = m_pp.platinum_bank; + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_OP_Barter(const EQApplicationPacket *app) +{ + + if (app->size < 4) + { + LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); + DumpPacket(app); + return; + } + + char* Buf = (char *)app->pBuffer; + + // The first 4 bytes of the packet determine the action. A lot of Barter packets require the + // packet the client sent, sent back to it as an acknowledgement. + // + uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); + + _pkt(TRADING__BARTER, app); + + switch (Action) + { + + case Barter_BuyerSearch: + { + BuyerItemSearch(app); + break; + } + + case Barter_SellerSearch: + { + BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; + SendBuyerResults(bsr->SearchString, bsr->SearchID); + break; + } + + case Barter_BuyerModeOn: + { + if (!Trader) { + ToggleBuyerMode(true); + } + else { + Buf = (char *)app->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); + Message(13, "You cannot be a Trader and Buyer at the same time."); + } + QueuePacket(app); + break; + } + + case Barter_BuyerModeOff: + { + QueuePacket(app); + ToggleBuyerMode(false); + break; + } + + case Barter_BuyerItemUpdate: + { + UpdateBuyLine(app); + break; + } + + case Barter_BuyerItemRemove: + { + BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; + database.RemoveBuyLine(CharacterID(), bris->BuySlot); + QueuePacket(app); + break; + } + + case Barter_SellItem: + { + SellToBuyer(app); + break; + } + + case Barter_BuyerInspectBegin: + { + ShowBuyLines(app); + break; + } + + case Barter_BuyerInspectEnd: + { + BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer; + Client *Buyer = entity_list.GetClientByID(bir->BuyerID); + if (Buyer) + Buyer->WithCustomer(0); + + break; + } + + case Barter_BarterItemInspect: + { + BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Welcome: + { + SendBazaarWelcome(); + break; + } + + case Barter_WelcomeMessageUpdate: + { + BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; + SetBuyerWelcomeMessage(bwmu->WelcomeMessage); + break; + } + + case Barter_BuyerItemInspect: + { + BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Unknown23: + { + // Sent by SoD client for no discernible reason. + break; + } + + default: + Message(13, "Unrecognised Barter action."); + _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); + + } +} + +void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BazaarInspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", + sizeof(BazaarInspect_Struct), app->size); + return; + } + + BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bis->ItemID); + + if (!item) { + Message(13, "Error: This item does not exist!"); + return; + } + + ItemInst* inst = database.CreateItem(item); + + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + + return; +} + +void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) +{ + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(BazaarSearch_Struct)) { + + BazaarSearch_Struct* bss = (BazaarSearch_Struct*)app->pBuffer; + + this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, + bss->Name, bss->MinPrice * 1000, bss->MaxPrice * 1000); + } + else if (app->size == sizeof(BazaarWelcome_Struct)) { + + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; + + if (bws->Beginning.Action == BazaarWelcome) + SendBazaarWelcome(); + } + else if (app->size == sizeof(NewBazaarInspect_Struct)) { + + NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(nbis->Name); + if (c) { + ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); + if (inst) + SendItemPacket(0, inst, ItemPacketViewLink); + } + return; + } + else { + _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); + LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); + } + + return; +} + +void Client::Handle_OP_Begging(const EQApplicationPacket *app) +{ + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + brs->Result = 0; + FastQueuePacket(&outapp); + return; + } + + if (!HasSkill(SkillBegging) || !GetTarget()) + return; + + if (GetTarget()->GetClass() == LDON_TREASURE) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + + brs->Result = 0; // Default, Fail. + if (GetTarget() == this) + { + FastQueuePacket(&outapp); + return; + } + + int RandomChance = MakeRandomInt(0, 100); + + int ChanceToAttack = 0; + + if (GetLevel() > GetTarget()->GetLevel()) + ChanceToAttack = MakeRandomInt(0, 15); + else + ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel()) * 10) - 5, ((this->GetTarget()->GetLevel() - this->GetLevel()) * 10)); + + if (ChanceToAttack < 0) + ChanceToAttack = -ChanceToAttack; + + if (RandomChance < ChanceToAttack) + { + GetTarget()->Attack(this); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + uint16 CurrentSkill = GetSkill(SkillBegging); + + float ChanceToBeg = ((float)(CurrentSkill / 700.0f) + 0.15f) * 100; + + if (RandomChance < ChanceToBeg) + { + brs->Amount = MakeRandomInt(1, 10); + // This needs some work to determine how much money they can beg, based on skill level etc. + if (CurrentSkill < 50) + { + brs->Result = 4; // Copper + AddMoneyToPP(brs->Amount, false); + } + else + { + brs->Result = 3; // Silver + AddMoneyToPP(brs->Amount * 10, false); + } + + } + QueuePacket(outapp); + safe_delete(outapp); + CheckIncreaseSkill(SkillBegging, nullptr, -10); +} + +void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BindWound_Struct)){ + LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); + DumpPacket(app); + } + BindWound_Struct* bind_in = (BindWound_Struct*)app->pBuffer; + Mob* bindmob = entity_list.GetMob(bind_in->to); + if (!bindmob){ + LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); + } + else { + LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); + BindWound(bindmob, true); + } + return; +} + +void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + + std::set::iterator Iterator; + + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + if (bbs->Initialise == 1) + { + BlockedBuffs->clear(); + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if ((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) + { + if (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) + BlockedBuffs->insert(bbs->SpellID[i]); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 1; + obbs->Flags = 0x54; + obbs->Count = BlockedBuffs->size(); + + unsigned int Element = 0; + + Iterator = BlockedBuffs->begin(); + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + return; + } + + if ((bbs->Initialise == 0) && (bbs->Count > 0)) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x54; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if (!IsBeneficialSpell(bbs->SpellID[i])) + continue; + + if ((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) + BlockedBuffs->insert(bbs->SpellID[i]); + } + obbs->Count = BlockedBuffs->size(); + + Iterator = BlockedBuffs->begin(); + + unsigned int Element = 0; + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) +{ + + if (app->size <= 5) + return; + + char *boatname; + boatname = new char[app->size - 3]; + memset(boatname, 0, app->size - 3); + memcpy(boatname, app->pBuffer, app->size - 4); + + Mob* boat = entity_list.GetMob(boatname); + if (boat) + this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat + safe_delete_array(boatname); + return; +} + +void Client::Handle_OP_Buff(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpellBuffFade_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); + DumpPacket(app); + return; + } + + SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*)app->pBuffer; + uint32 spid = sbf->spellid; + mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); + + //something about IsDetrimentalSpell() crashes this portion of code.. + //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and + //isdetrimentalspell() is much more complex + if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) + QueuePacket(app); + else + BuffFadeBySpellID(spid); + + return; +} + +void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) +{ + // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets + // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. + // + VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); + + BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; + + Mob *m = nullptr; + + if (brrs->EntityID == GetID()) + m = this; + else if (brrs->EntityID == GetPetID()) + m = GetPet(); + + if (!m) + return; + + if (brrs->SlotID > (uint32)m->GetMaxTotalSlots()) + return; + + uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); + + if (SpellID && IsBeneficialSpell(SpellID)) + m->BuffFadeBySlot(brrs->SlotID, true); +} + +void Client::Handle_OP_Bug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BugStruct)) + printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); + else{ + BugStruct* bug = (BugStruct*)app->pBuffer; + database.UpdateBug(bug); + } + return; +} + +void Client::Handle_OP_Camp(const EQApplicationPacket *app) +{ +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + Bot::BotHealRotationsClear(this); + Bot::BotOrderCampAll(this); +#endif + if (IsLFP()) + worldserver.StopLFP(CharacterID()); + + if (GetGM()) + { + OnDisconnect(true); + return; + } + camp_timer.Start(29000, true); + return; +} + +void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(CancelTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", + sizeof(CancelTask_Struct), app->size); + DumpPacket(app); + return; + } + CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->CancelTask(this, cts->SequenceNumber); +} + +void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CancelTrade_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); + return; + } + Mob* with = trade->With(); + if (with && with->IsClient()) { + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + + // Forward cancel packet to other client + msg->fromid = with->GetID(); + //msg->action = 1; + + with->CastToClient()->QueuePacket(app); + + // Put trade items/cash back into inventory + FinishTrade(this); + trade->Reset(); + } + else if (with){ + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + msg->fromid = with->GetID(); + QueuePacket(app); + FinishTrade(this); + trade->Reset(); + } + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CastSpell_Struct)) { + std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; + return; + } + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + //Message(13, "You cant cast right now, you arent in control of yourself!"); + return; + } + + CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; + +#ifdef _EQDEBUG + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); +#endif + LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); + + std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; + + /* Memorized Spell */ + if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ + + uint16 spell_to_cast = 0; + if (castspell->slot < MAX_PP_MEMSPELL) { + spell_to_cast = m_pp.mem_spells[castspell->slot]; + if (spell_to_cast != castspell->spell_id) { + InterruptSpell(castspell->spell_id); //CHEATER!!! + return; + } + } + else if (castspell->slot >= MAX_PP_MEMSPELL) { + InterruptSpell(); + return; + } + + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + /* Spell Slot or Potion Belt Slot */ + else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast + { + //discipline, using the item spell slot + if (castspell->inventoryslot == 0xFFFFFFFF) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + } + return; + } + else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check + { + const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field + //bool cancast = true; + if (inst && inst->IsType(ItemClassCommon)) + { + const Item_Struct* item = inst->GetItem(); + if (item->Click.Effect != (uint32)castspell->spell_id) + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); + InterruptSpell(castspell->spell_id); //CHEATER!! + return; + } + + if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (item->Click.Level2 > 0) + { + if (GetLevel() >= item->Click.Level2) + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + else + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); + Message(0, "Error: level not high enough.", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + } + else + { + Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + /* Discipline */ + else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + return; + } + } + /* ABILITY cast (LoH and Harm Touch) */ + else if (castspell->slot == ABILITY_SPELL_SLOT) { + uint16 spell_to_cast = 0; + + if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { + if (!p_timers.Expired(&database, pTimerLayHands)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + spell_to_cast = SPELL_LAY_ON_HANDS; + p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); + } + else if ((castspell->spell_id == SPELL_HARM_TOUCH + || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { + if (!p_timers.Expired(&database, pTimerHarmTouch)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + + // determine which version of HT we are casting based on level + if (GetLevel() < 40) + spell_to_cast = SPELL_HARM_TOUCH; + else + spell_to_cast = SPELL_HARM_TOUCH2; + + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + + if (spell_to_cast > 0) // if we've matched LoH or HT, cast now + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + return; +} + +void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +{ + ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app->pBuffer; + + if (app->size < sizeof(ChannelMessage_Struct)) { + std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; + return; + } + if (IsAIControlled()) { + Message(13, "You try to speak but cant move your mouth!"); + return; + } + + ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + return; +} + +void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); + + DumpPacket(app); + + return; + } + + bool Pet = app->pBuffer[0]; + + if (Pet) + PetBlockedBuffs.clear(); + else + PlayerBlockedBuffs.clear(); + + QueuePacket(app); +} + +void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) +{ + + if (app->size != 0) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", + app->size); + + DumpPacket(app); + + return; + } + + Group *g = GetGroup(); + + if (g) + g->ClearAllNPCMarks(); +} + +void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) +{ + ChangeLastName(""); +} + +void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickDoor_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); + return; + } + ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; + Doors* currentdoor = entity_list.FindDoor(cd->doorid); + if (!currentdoor) + { + Message(0, "Unable to find door, please notify a GM (DoorID: %i).", cd->doorid); + return; + } + + char buf[20]; + snprintf(buf, 19, "%u", cd->doorid); + buf[19] = '\0'; + std::vector args; + args.push_back(currentdoor); + parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); + + currentdoor->HandleClick(this, 0); + return; +} + +void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickObject_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", + sizeof(ClickObject_Struct), app->size); + return; + } + + ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(click_object->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + + object->HandleClick(this, click_object); + + std::vector args; + args.push_back(object); + + char buf[10]; + snprintf(buf, 9, "%u", click_object->drop_id); + buf[9] = '\0'; + parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); + } + + // Observed in RoF after OP_ClickObjectAction: + //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + //QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. + // Not completely sure if 0 sized is for this or for closing objects as commented out below + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + + return; + + // RoF sends a 0 sized packet for closing objects + /* + Object* object = GetTradeskillObject(); + if (object) { + object->CastToObject()->Close(); + } + */ + } + else + { + if (app->size != sizeof(ClickObjectAction_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", + sizeof(ClickObjectAction_Struct), app->size); + return; + } + + ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; + Entity* entity = entity_list.GetEntityObject(oos->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + if (oos->open == 0) { + object->Close(); + } + else { + LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); + } + } + else { + LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); + } + } + + SetTradeskillObject(nullptr); + + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClientError(const EQApplicationPacket *app) +{ + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); + return; +} + +void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) +{ + return; } void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) @@ -1753,69 +4587,38 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) return; } -void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +/* +void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) { - if (app->size != 4) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); - return; - } - - if (app->pBuffer[0] == 0) - { - auto_attack = false; - if (IsAIControlled()) - return; - attack_timer.Disable(); - ranged_timer.Disable(); - attack_dw_timer.Disable(); - - aa_los_me.x = 0; - aa_los_me.y = 0; - aa_los_me.z = 0; - aa_los_me_heading = 0; - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - } - else if (app->pBuffer[0] == 1) - { - auto_attack = true; - auto_fire = false; - if (IsAIControlled()) - return; - SetAttackTimer(); - - if(GetTarget()) - { - aa_los_them_mob = GetTarget(); - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = aa_los_them_mob->GetX(); - aa_los_them.y = aa_los_them_mob->GetY(); - aa_los_them.z = aa_los_them_mob->GetZ(); - los_status = CheckLosFN(aa_los_them_mob); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - else - { - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - los_status = false; - los_status_facing = false; - } - } +if (app->size != sizeof(CloseContainer_Struct)) { +LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", +sizeof(CloseContainer_Struct), app->size); +return; } -void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +SetTradeskillObject(nullptr); + +ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; +Entity* entity = entity_list.GetEntityObject(oos->drop_id); +if (entity && entity->IsObject()) { +Object* object = entity->CastToObject(); +object->Close(); +} +return; +} +*/ + +void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatAbility_Struct)) { + std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; + return; + } + OPCombatAbility(app); + return; +} + +void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app) { return; } @@ -1863,494 +4666,158 @@ void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) return; } -void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +void Client::Handle_OP_Consider(const EQApplicationPacket *app) { - Handle_OP_TargetCommand(app); -} + if (app->size != sizeof(Consider_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); + return; + } + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; + Mob* tmob = entity_list.GetMob(conin->targetid); + if (tmob == 0) + return; -void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + if (tmob->GetClass() == LDON_TREASURE) + { + Message(15, "%s", tmob->GetCleanName()); return; } - if(GetTarget()) - { - GetTarget()->IsTargeted(-1); - } - - // Locate and cache new target - ClientTarget_Struct* ct=(ClientTarget_Struct*)app->pBuffer; - pClientSideTarget = ct->new_target; - if(!IsAIControlled()) - { - Mob *nt = entity_list.GetMob(ct->new_target); - if(nt) - { - SetTarget(nt); - if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || - (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || - (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) - nt->SendBuffsToClient(this); - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(0); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(0); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(0); - - return; - } - } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Consider, sizeof(Consider_Struct)); + Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; + con->playerid = GetID(); + con->targetid = conin->targetid; + if (tmob->IsNPC()) + con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - return; + con->faction = 1; + con->level = GetLevelCon(tmob->GetLevel()); + if (zone->IsPVPZone()) { + if (!tmob->IsNPC()) + con->pvpcon = tmob->CastToClient()->GetPVP(); } - // HoTT - if (GetTarget() && GetTarget()->GetTarget()) + // Mongrel: If we're feigned show NPC as indifferent + if (tmob->IsNPC()) { - SetHoTT(GetTarget()->GetTarget()->GetID()); - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - } - else - { - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); + if (GetFeigned()) + con->faction = FACTION_INDIFFERENT; } - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(GetTarget()); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(GetTarget()); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(GetTarget()); - - // For /target, send reject or success packet - if (app->GetOpcode() == OP_TargetCommand) { - if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { - if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - //Targeting something we shouldn't with /target - //but the client allows this without MQ so you don't flag it - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - QueuePacket(app); - EQApplicationPacket hp_app; - GetTarget()->IsTargeted(1); - GetTarget()->CreateHPPacket(&hp_app); - QueuePacket(&hp_app, false); - } - else + if (!(con->faction == FACTION_SCOWLS)) + { + if (tmob->IsNPC()) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); + if (tmob->CastToNPC()->IsOnHatelist(this)) + con->faction = FACTION_THREATENLY; } } - else - { - if(GetTarget()) - { - if(GetGM()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsAssistExempted()) - { - GetTarget()->IsTargeted(1); - SetAssistExemption(false); - return; - } - else if(GetTarget()->IsClient()) - { - //make sure this client is in our raid/group - GetTarget()->IsTargeted(1); - return; - } - else if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", - GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget((Mob*)nullptr); - return; - } - else if(IsPortExempted()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsSenseExempted()) - { - GetTarget()->IsTargeted(1); - SetSenseExemption(false); - return; - } - else if(IsXTarget(GetTarget())) - { - GetTarget()->IsTargeted(1); - return; - } - else if(GetBindSightTarget()) - { - if(GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - } - } - else if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - GetTarget()->IsTargeted(1); - } + if (con->faction == FACTION_APPREHENSIVE) { + con->faction = FACTION_SCOWLS; } + else if (con->faction == FACTION_DUBIOUS) { + con->faction = FACTION_THREATENLY; + } + else if (con->faction == FACTION_SCOWLS) { + con->faction = FACTION_APPREHENSIVE; + } + else if (con->faction == FACTION_THREATENLY) { + con->faction = FACTION_DUBIOUS; + } + + mod_consider(tmob, con); + + QueuePacket(outapp); + // only wanted to check raid target once + // and need con to still be around so, do it here! + if (tmob->IsRaidTarget()) { + uint32 color = 0; + switch (con->level) { + case CON_GREEN: + color = 2; + break; + case CON_LIGHTBLUE: + color = 10; + break; + case CON_BLUE: + color = 4; + break; + case CON_WHITE: + color = 10; + break; + case CON_YELLOW: + color = 15; + break; + case CON_RED: + color = 13; + break; + } + SendColoredText(color, std::string("This creature would take an army to defeat!")); + } + safe_delete(outapp); return; } -void Client::Handle_OP_Shielding(const EQApplicationPacket *app) +void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) { - if (app->size != sizeof(Shielding_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + if (app->size != sizeof(Consider_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); return; } - if(GetClass() != WARRIOR) - { - return; - } - - if (shield_target) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - END_SHIELDING, GetName(), shield_target->GetName()); - for (int y = 0; y < 2; y++) - { - if (shield_target->shielder[y].shielder_id == GetID()) - { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; + Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); + if (tcorpse && tcorpse->IsNPCCorpse()) { + uint32 min; uint32 sec; uint32 ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds / 60 drop .00 + char val1[20] = { 0 }; + char val2[20] = { 0 }; + Message_StringID(10, CORPSE_DECAY1, ConvertArray(min, val1), ConvertArray(sec, val2)); + } + else { + Message_StringID(10, CORPSE_DECAY_NOW); } } - Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - shield_target = entity_list.GetMob(shield->target_id); - bool ack = false; - ItemInst* inst = GetInv().GetItem(MainSecondary); - if (!shield_target) - return; - if (inst) - { - const Item_Struct* shield = inst->GetItem(); - if (shield && shield->ItemType == ItemTypeShield) - { - for (int x = 0; x < 2; x++) - { - if (shield_target->shielder[x].shielder_id == 0) - { - entity_list.MessageClose_StringID(this ,false, 100, 0, - START_SHIELDING, GetName(), shield_target->GetName()); - shield_target->shielder[x].shielder_id = GetID(); - int shieldbonus = shield->AC*2; - switch (GetAA(197)) - { - case 1: - shieldbonus = shieldbonus * 115 / 100; - break; - case 2: - shieldbonus = shieldbonus * 125 / 100; - break; - case 3: - shieldbonus = shieldbonus * 150 / 100; - break; - } - shield_target->shielder[x].shielder_bonus = shieldbonus; - shield_timer.Start(); - ack = true; - break; - } + else if (tcorpse && tcorpse->IsPlayerCorpse()) { + uint32 day, hour, min, sec, ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds + hour = (ttime / 3600000) % 24; // Total hours + day = ttime / 86400000; // Total Days + if (day) + Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); + else if (hour) + Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); + + Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed() ? "cannot" : "can"); + /* + hour = 0; + + if((ttime = tcorpse->GetResTime()) != 0) { + sec = (ttime/1000)%60; // Total seconds + min = (ttime/60000)%60; // Total seconds + hour = (ttime/3600000)%24; // Total hours + if(hour) + Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); } + else { + Message_StringID(0, CORPSE_TOO_OLD); + } + */ } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; + else { + Message_StringID(10, CORPSE_DECAY_NOW); } } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - if (!ack) - { - Message_StringID(0, ALREADY_SHIELDED); - shield_target = 0; - return; - } - return; -} - -void Client::Handle_OP_Jump(const EQApplicationPacket *app) -{ - SetEndurance(GetEndurance() - (GetLevel()<20?(225*GetLevel()/100):50)); - return; -} - -void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(EntityId_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); - return; - } - EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; - Mob * m = entity_list.GetMob(ent->entity_id); - if(m && m->IsNPC()) - { - std::map::iterator it; - it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); - if(it != zone->adventure_entry_list_flavor.end()) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); - strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); - FastQueuePacket(&outapp); - } - else - { - if(m->CastToNPC()->GetAdventureTemplate() != 0) - { - std::string text = "Choose your difficulty and preferred adventure type."; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); - strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); - FastQueuePacket(&outapp); - } - } - } -} - -void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); - return; - } - - if(IsOnAdventure()) - { - return; - } - - if(!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) - { - return; - } - - if(GetPendingAdventureRequest()) - { - return; - } - - AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; - uint8 group_members = 0; - Raid *r = nullptr; - Group *g = nullptr; - - if(IsRaidGrouped()) - { - r = GetRaid(); - group_members = r->RaidCount(); - } - else if(IsGrouped()) - { - g = GetGroup(); - group_members = g->GroupCount(); - } - else - { - return; - } - - if(group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) - { - return; - } - - Mob* m = entity_list.GetMob(ars->entity_id); - uint32 template_id = 0; - if(m && m->IsNPC()) - { - template_id = m->CastToNPC()->GetAdventureTemplate(); - } - else - { - return; - } - - ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct) + (64 * group_members)); - ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; - sar->member_count = group_members; - sar->risk = ars->risk; - sar->type = ars->type; - sar->template_id = template_id; - strcpy(sar->leader, GetName()); - - if(IsRaidGrouped()) - { - int i = 0; - for(int x = 0; x < 72; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = r->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - else - { - int i = 0; - for(int x = 0; x < 6; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = g->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - - packet->Deflate(); - worldserver.SendPacket(packet); - delete packet; - p_timers.Start(pTimerStartAdventureTimer, 5); -} - -void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) -{ - if(app->size < sizeof(bool)) - { - return; - } - - if(GetPendingAdventureCreate()) - { - return; - } - - if(IsOnAdventure()) - { - return; - } - - bool* p = (bool*)app->pBuffer; - if(*p == true) - { - ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct) + (64 * adv_requested_member_count)); - ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; - strcpy(sac->leader, GetName()); - sac->id = adv_requested_id; - sac->theme = adv_requested_theme; - sac->member_count = adv_requested_member_count; - memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); - pack->Deflate(); - worldserver.SendPacket(pack); - delete pack; - PendingAdventureCreate(); - ClearPendingAdventureData(); - } - else - { - ClearPendingAdventureData(); - } -} - -void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) -{ - if(!IsOnAdventure()) - { - return; - } - LeaveAdventure(); } void Client::Handle_OP_Consume(const EQApplicationPacket *app) @@ -2361,9 +4828,9 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; - if(pcs->type == 0x01) + if (pcs->type == 0x01) { - if(m_pp.hunger_level > 6000) + if (m_pp.hunger_level > 6000) { EQApplicationPacket *outapp; outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); @@ -2376,9 +4843,9 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } } - else if(pcs->type == 0x02) + else if (pcs->type == 0x02) { - if(m_pp.thirst_level > 6000) + if (m_pp.thirst_level > 6000) { EQApplicationPacket *outapp; outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); @@ -2393,7 +4860,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) } ItemInst *myitem = GetInv().GetItem(pcs->slot); - if(myitem == nullptr) { + if (myitem == nullptr) { LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); return; } @@ -2424,905 +4891,457 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } -void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) { - if (app->size != sizeof(ItemVerifyRequest_Struct)) + if (app->size != sizeof(ControlBoat_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); + return; + } + ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; + Mob* boat = entity_list.GetMob(cbs->boatId); + if (boat == 0) + return; // do nothing if the boat isn't valid + + if (!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); return; } - ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; - int32 slot_id; - int32 target_id; - int32 spell_id = 0; - slot_id = request->slot; - target_id = request->target; - - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); - ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; - reply->slot = slot_id; - reply->target = target_id; - - QueuePacket(outapp); - safe_delete(outapp); - - - if (IsAIControlled()) { - this->Message_StringID(13,NOT_IN_CONTROL); - return; - } - - if(slot_id < 0) { - LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i",GetName(),request->slot); - return; - } - - const ItemInst* inst = m_inv[slot_id]; - if (!inst) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - const Item_Struct* item = inst->GetItem(); - if (!item) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - spell_id = item->Click.Effect; - - if - ( - spell_id > 0 && - ( - !IsValidSpell(spell_id) || - casting_spell_id || - delaytimer || - spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || - DivineAura() || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) || - (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) - ) - ) - { - SendSpellBarEnable(spell_id); - return; - } - - LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - - if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check - { - ItemInst* p_inst = (ItemInst*)inst; - - parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - int r; - bool tryaug = false; - ItemInst* clickaug = 0; - Item_Struct* augitem = 0; - - for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { - const ItemInst* aug_i = inst->GetAugment(r); - if(!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if(!aug) - continue; - - if ( (aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2) ) - { - tryaug = true; - clickaug = (ItemInst*)aug_i; - augitem = (Item_Struct*)aug; - spell_id = aug->Click.Effect; - break; - } - } - - if((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) - { - LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s",GetName()); - } - else if (inst->IsType(ItemClassCommon)) - { - if(item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) - { - DeleteItemInInventory(slot_id, 1, true); - TrainDiscipline(item->ID); - } - else if(item->ItemType == ItemTypeSpell) - { - return; - } - else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (inst->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= item->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else if (tryaug) - { - if (clickaug->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= augitem->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else - { - if(GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(),GetClass())) - { - if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - else - { - //This is food/drink - consume it - if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeAlcohol) - { -#if EQDEBUG >= 1 - LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); -#endif - // This Seems to be handled in OP_DeleteItem handling - //DeleteItemInInventory(slot_id, 1, false); - //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - //Should add intoxication level to the PP at some point - //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); - } - - if (m_pp.hunger_level > 6000) - m_pp.hunger_level = 6000; - if (m_pp.thirst_level > 6000) - m_pp.thirst_level = 6000; - - EQApplicationPacket *outapp2; - outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - - QueuePacket(outapp2); - safe_delete(outapp2); - } - - } - else - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - } - } - else - { - Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); - } - - return; -} - -void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AdventureMerchant_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); - return; - } - std::stringstream ss(std::stringstream::in | std::stringstream::out); - - uint8 count = 0; - AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; - uint32 merchantid = 0; - - Mob* tmp = entity_list.GetMob(eid->entity_id); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - tmp->CastToNPC()->FaceTarget(this->CastToMob()); - - const Item_Struct *item = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end() && count<255;++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - uint32 theme; - if(item->LDoNTheme > 16) - { - theme = 0; - } - else if(item->LDoNTheme & 16) - { - theme = 5; - } - else if(item->LDoNTheme & 8) - { - theme = 4; - } - else if(item->LDoNTheme & 4) - { - theme = 3; - } - else if(item->LDoNTheme & 2) - { - theme = 2; - } - else if(item->LDoNTheme & 1) - { - theme = 1; - } - else - { - theme = 0; - } - ss << "^" << item->Name << "|"; - ss << item->ID << "|"; - ss << item->LDoNPrice << "|"; - ss << theme << "|"; - ss << "0|"; - ss << "1|"; - ss << item->Races << "|"; - ss << item->Classes; - count++; - } - } - //Count - //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse,ss.str().size()+2); - outapp->pBuffer[0] = count; - strn0cpy((char*)&outapp->pBuffer[1],ss.str().c_str(),ss.str().size()); - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Adventure_Purchase_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); - return; - } - - Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; -/* - Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) - if(ldon_points_available >= item ldonpointcost) - { - give item (67 00 00 00 for the packettype using opcode 0x02c5) - ldon_points_available -= ldonpointcost; - } -*/ - uint32 merchantid = 0; - Mob* tmp = entity_list.GetMob(aps->npcid); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - const Item_Struct* item = nullptr; - bool found = false; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - if(item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - found = true; - break; - } - } - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(aps->Type == LDoNMerchant) - { - if(m_pp.ldon_points_available < int32(item->LDoNPrice)) { - Message(13, "You cannot afford that item."); - return; - } - - if(item->LDoNTheme <= 16) - { - if(item->LDoNTheme & 16) - { - if(m_pp.ldon_points_tak < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 8) - { - if(m_pp.ldon_points_ruj < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 4) - { - if(m_pp.ldon_points_mmc < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 2) - { - if(m_pp.ldon_points_mir < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 1) - { - if(m_pp.ldon_points_guk < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - } - } - else if(aps->Type == DiscordMerchant) - { - if(GetPVPPoints() < item->LDoNPrice) - { - Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(aps->Type == NorrathsKeepersMerchant) - { - if(GetRadiantCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(aps->Type == DarkReignMerchant) - { - if(GetEbonCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else - { - Message(13, "Unknown Adventure Merchant type."); - return; - } - - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - - if(aps->Type == LDoNMerchant) - { - int32 requiredpts = (int32)item->LDoNPrice*-1; - - if(!UpdateLDoNPoints(requiredpts, 6)) - return; - } - else if(aps->Type == DiscordMerchant) - { - SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); - SendPVPStats(); - } - else if(aps->Type == NorrathsKeepersMerchant) - { - SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - else if(aps->Type == DarkReignMerchant) - { - SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - Save(1); -} - -void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); - if (tcorpse && tcorpse->IsNPCCorpse()) { - uint32 min; uint32 sec; uint32 ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds / 60 drop .00 - char val1[20]={0}; - char val2[20]={0}; - Message_StringID(10,CORPSE_DECAY1,ConvertArray(min,val1),ConvertArray(sec,val2)); + if (cbs->TakeControl) { + // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. + if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { + boat->SetTarget(this); } else { - Message_StringID(10,CORPSE_DECAY_NOW); + this->Message_StringID(13, IN_USE); + return; } } - else if (tcorpse && tcorpse->IsPlayerCorpse()) { - uint32 day, hour, min, sec, ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - day = ttime/86400000; // Total Days - if(day) - Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); - else if(hour) - Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); + else + boat->SetTarget(0); - Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed()?"cannot":"can"); - /* - hour = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ControlBoat, 0); + FastQueuePacket(&outapp); + safe_delete(outapp); + // have the boat signal itself, so quests can be triggered by boat use + boat->CastToNPC()->SignalNPC(0); +} - if((ttime = tcorpse->GetResTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - if(hour) - Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); - } - else { - Message_StringID(0, CORPSE_TOO_OLD); - } - */ +void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) +{ + if (DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) + { + Message_StringID(13, CORPSEDRAG_LIMIT); + return; + } + + VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); + + CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; + + Mob* corpse = entity_list.GetMob(cds->CorpseName); + + if (!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) + return; + + Client *c = entity_list.FindCorpseDragger(corpse->GetID()); + + if (c) + { + if (c == this) + Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); + else + Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); + + return; + } + + if (!corpse->CastToCorpse()->Summon(this, false, true)) + return; + + DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); + + Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); +} + +void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) +{ + if (app->size == 1) + { + Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); + ClearDraggedCorpses(); + return; + } + + for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) + { + if (!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) + { + Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); + Iterator = DraggedCorpses.erase(Iterator); + return; + } + } +} + +void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) +{ + DropItem(MainCursor); + return; +} + +void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); + CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; + + if (cr->type == 5) { + if (cr->amount > GetEbonCrystals()) { + SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); + m_pp.currentEbonCrystals = 0; + m_pp.careerEbonCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); } else { - Message_StringID(10,CORPSE_DECAY_NOW); + SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); + m_pp.currentEbonCrystals -= cr->amount; + m_pp.careerEbonCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); + } + } + else if (cr->type == 4) { + if (cr->amount > GetRadiantCrystals()) { + SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); + m_pp.currentRadCrystals = 0; + m_pp.careerRadCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); + } + else { + SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); + m_pp.currentRadCrystals -= cr->amount; + m_pp.careerRadCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); } } } -void Client::Handle_OP_Consider(const EQApplicationPacket *app) +void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) { - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); - return; + uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + if ((ebon + radiant) > 0) { + AddCrystals(radiant, ebon); } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Mob* tmob = entity_list.GetMob(conin->targetid); - if (tmob == 0) - return; +} - if(tmob->GetClass() == LDON_TREASURE) - { - Message(15, "%s", tmob->GetCleanName()); +void Client::Handle_OP_Damage(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatDamage_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, + sizeof(CombatDamage_Struct)); + DumpPacket(app); return; } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Consider, sizeof(Consider_Struct)); - Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; - con->playerid = GetID(); - con->targetid = conin->targetid; - if(tmob->IsNPC()) - con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity,(tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction():0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity - else - con->faction = 1; - con->level = GetLevelCon(tmob->GetLevel()); - if(zone->IsPVPZone()) { - if (!tmob->IsNPC() ) - con->pvpcon = tmob->CastToClient()->GetPVP(); - } - - // Mongrel: If we're feigned show NPC as indifferent - if (tmob->IsNPC()) - { - if (GetFeigned()) - con->faction = FACTION_INDIFFERENT; - } - - if(!(con->faction == FACTION_SCOWLS)) - { - if(tmob->IsNPC()) - { - if(tmob->CastToNPC()->IsOnHatelist(this)) - con->faction = FACTION_THREATENLY; - } - } - - if(con->faction == FACTION_APPREHENSIVE) { - con->faction = FACTION_SCOWLS; - } else if(con->faction == FACTION_DUBIOUS) { - con->faction = FACTION_THREATENLY; - } else if(con->faction == FACTION_SCOWLS) { - con->faction = FACTION_APPREHENSIVE; - } else if(con->faction == FACTION_THREATENLY) { - con->faction = FACTION_DUBIOUS; - } - - mod_consider(tmob, con); - - QueuePacket(outapp); - safe_delete(outapp); + // Broadcast to other clients + CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; + //dont send to originator of falling damage packets + entity_list.QueueClients(this, app, (damage->type == DamageTypeFalling)); return; } -void Client::Handle_OP_Begging(const EQApplicationPacket *app) +void Client::Handle_OP_Death(const EQApplicationPacket *app) { - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13,"Ability recovery time not yet met."); + if (app->size != sizeof(Death_Struct)) + return; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - brs->Result = 0; - FastQueuePacket(&outapp); + Death_Struct* ds = (Death_Struct*)app->pBuffer; + + //I think this attack_skill value is really a value from SkillDamageTypes... + if (ds->attack_skill > HIGHEST_SKILL) { + mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); return; } - if(!HasSkill(SkillBegging) || !GetTarget()) + if (GetHP() > 0) return; - if(GetTarget()->GetClass() == LDON_TREASURE) - return; + Mob* killer = entity_list.GetMob(ds->killer_id); + Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); + return; +} - p_timers.Start(pTimerBeggingPickPocket, 8); +void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) +{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - - brs->Result = 0; // Default, Fail. - if(GetTarget() == this) + if (app->size != sizeof(DelegateAbility_Struct)) { - FastQueuePacket(&outapp); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", + sizeof(DelegateAbility_Struct), app->size); + + DumpPacket(app); + return; } - int RandomChance = MakeRandomInt(0 ,100); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; - int ChanceToAttack = 0; + Group *g = GetGroup(); - if(GetLevel() > GetTarget()->GetLevel()) - ChanceToAttack = MakeRandomInt(0, 15); - else - ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel())*10)-5,((this->GetTarget()->GetLevel() - this->GetLevel())*10)); + if (!g) return; - if(ChanceToAttack < 0) - ChanceToAttack = -ChanceToAttack; - - if(RandomChance < ChanceToAttack) + switch (das->DelegateAbility) { - GetTarget()->Attack(this); - QueuePacket(outapp); - safe_delete(outapp); + case 0: + { + g->DelegateMainAssist(das->Name); + break; + } + case 1: + { + g->DelegateMarkNPC(das->Name); + break; + } + case 2: + { + g->DelegateMainTank(das->Name); + break; + } + case 3: + { + g->DelegatePuller(das->Name); + break; + } + default: + break; + } +} + +void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteItem_Struct)) { + std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; return; } - uint16 CurrentSkill = GetSkill(SkillBegging); + DeleteItem_Struct* alc = (DeleteItem_Struct*)app->pBuffer; + const ItemInst *inst = GetInv().GetItem(alc->from_slot); + if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { + entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); + CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); - float ChanceToBeg=((float)(CurrentSkill/700.0f) + 0.15f) * 100; + int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); + int16 IntoxicationIncrease; - if(RandomChance < ChanceToBeg) - { - brs->Amount = MakeRandomInt(1, 10); - // This needs some work to determine how much money they can beg, based on skill level etc. - if(CurrentSkill < 50) - { - brs->Result = 4; // Copper - AddMoneyToPP(brs->Amount, false); - } + if (GetClientVersion() < EQClientSoD) + IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; else - { - brs->Result = 3; // Silver - AddMoneyToPP(brs->Amount * 10, false); - } + IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; + if (IntoxicationIncrease < 0) + IntoxicationIncrease = 1; + + m_pp.intoxication += IntoxicationIncrease; + + if (m_pp.intoxication > 200) + m_pp.intoxication = 200; } - QueuePacket(outapp); + DeleteItemInInventory(alc->from_slot, 1); + + return; +} + +void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) +{ + // The client will send this with his id when he zones, maybe when he disconnects too? + //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning + + //just make sure this gets out + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); + EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; + eid->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); safe_delete(outapp); - CheckIncreaseSkill(SkillBegging, nullptr, -10); + + hate_list.RemoveEnt(this->CastToMob()); + + Disconnect(); + return; } -void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) +void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) { -} - -void Client::Handle_OP_Surname(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Surname_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + if (app->size != sizeof(DeleteSpell_Struct)) return; - } - - if(!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) - { - Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); - return; - } - - if(GetLevel() < 20) - { - Message_StringID(15, SURNAME_LEVEL); - return; - } - - Surname_Struct* surname = (Surname_Struct*) app->pBuffer; - - char *c = nullptr; - bool first = true; - for(c = surname->lastname; *c; c++) - { - if(first) - { - *c = toupper(*c); - first = false; - } - else - { - *c = tolower(*c); - } - } - - if (strlen(surname->lastname) >= 20) { - Message_StringID(15, SURNAME_TOO_LONG); - return; - } - - if(!database.CheckNameFilter(surname->lastname, true)) - { - Message_StringID(15, SURNAME_REJECTED); - return; - } - - ChangeLastName(surname->lastname); - p_timers.Start(pTimerSurnameChange, 604800); EQApplicationPacket* outapp = app->Copy(); - outapp = app->Copy(); - surname = (Surname_Struct*) outapp->pBuffer; - surname->unknown0064=1; + DeleteSpell_Struct* dss = (DeleteSpell_Struct*)outapp->pBuffer; + + if (dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) + return; + + if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { + m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; + dss->success = 1; + } + else + dss->success = 0; + FastQueuePacket(&outapp); return; } -void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) +void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) { - ChangeLastName(""); -} + if (!HasSkill(SkillDisarmTraps)) + return; -void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); - *(uint32 *)outapp->pBuffer = GetID(); - entity_list.QueueCloseClients(this, outapp, true, 100.0); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Assist(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); + if (!p_timers.Expired(&database, pTimerDisarmTraps, false)) { + Message(13, "Ability recovery time not yet met."); return; } + int reuse = DisarmTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerDisarmTraps, reuse - 1); - EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(eid->entity_id); - - EQApplicationPacket* outapp = app->Copy(); - eid = (EntityId_Struct*)outapp->pBuffer; - if (RuleB(Combat, AssistNoTargetSelf)) - eid->entity_id = GetID(); - if (entity && entity->IsMob()) { - Mob *assistee = entity->CastToMob(); - if (assistee->GetTarget()) { - Mob *new_target = assistee->GetTarget(); - if (new_target && (GetGM() || - Dist(*assistee) <= TARGETING_RANGE)) { - SetAssistExemption(true); - eid->entity_id = new_target->GetID(); + Trap* trap = entity_list.FindNearbyTrap(this, 60); + if (trap && trap->detected) + { + int uskill = GetSkill(SkillDisarmTraps); + if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) + { + Message(MT_Skills, "You disarm a trap."); + trap->disarmed = true; + trap->chkarea_timer.Disable(); + trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var)) * 1000); + } + else + { + if (MakeRandomInt(0, 99) < 25){ + Message(MT_Skills, "You set off the trap while trying to disarm it!"); + trap->Trigger(this); + } + else{ + Message(MT_Skills, "You failed to disarm a trap."); } } - } - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); + CheckIncreaseSkill(SkillDisarmTraps, nullptr); return; } - QueuePacket(app); + Message(MT_Skills, "You did not find any traps close enough to disarm."); return; } -void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) +void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) { - if (app->size != sizeof(GMTrainee_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + + if (app->size != sizeof(DoGroupLeadershipAbility_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", + sizeof(DoGroupLeadershipAbility_Struct), app->size); + DumpPacket(app); - return; - } - OPGMTraining(app); - return; -} -void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainEnd_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); - DumpPacket(app); return; } - OPGMEndTraining(app); - return; -} -void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSkillChange_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); - DumpPacket(app); - return; + DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; + + switch (dglas->Ability) + { + case GroupLeadershipAbility_MarkNPC: + { + if (GetTarget()) + { + Group* g = GetGroup(); + if (g) + g->MarkNPC(GetTarget(), dglas->Parameter); + } + break; + } + + case groupAAInspectBuffs: + { + Mob *Target = GetTarget(); + + if (!Target || !Target->IsClient()) + return; + + Group *g = GetGroup(); + + if (!g || (g->GroupCount() < 3)) + return; + + Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); + + break; + } + + default: + break; } - OPGMTrainSkill(app); - return; } void Client::Handle_OP_DuelResponse(const EQApplicationPacket *app) { - if(app->size != sizeof(DuelResponse_Struct)) + if (app->size != sizeof(DuelResponse_Struct)) return; - DuelResponse_Struct* ds = (DuelResponse_Struct*) app->pBuffer; + DuelResponse_Struct* ds = (DuelResponse_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->target_id); Entity* initiator = entity_list.GetID(ds->entity_id); - if(!entity->IsClient() || !initiator->IsClient()) + if (!entity->IsClient() || !initiator->IsClient()) return; entity->CastToClient()->SetDuelTarget(0); entity->CastToClient()->SetDueling(false); initiator->CastToClient()->SetDuelTarget(0); initiator->CastToClient()->SetDueling(false); - if(GetID() == initiator->GetID()) - entity->CastToClient()->Message_StringID(10,DUEL_DECLINE,initiator->GetName()); + if (GetID() == initiator->GetID()) + entity->CastToClient()->Message_StringID(10, DUEL_DECLINE, initiator->GetName()); else - initiator->CastToClient()->Message_StringID(10,DUEL_DECLINE,entity->GetName()); + initiator->CastToClient()->Message_StringID(10, DUEL_DECLINE, entity->GetName()); return; } void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) + if (app->size != sizeof(Duel_Struct)) return; - Duel_Struct* ds = (Duel_Struct*) app->pBuffer; + Duel_Struct* ds = (Duel_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->duel_target); Entity* initiator = entity_list.GetID(ds->duel_initiator); if (entity && initiator && entity == this && initiator->IsClient()) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestDuel, sizeof(Duel_Struct)); - Duel_Struct* ds2 = (Duel_Struct*) outapp->pBuffer; + Duel_Struct* ds2 = (Duel_Struct*)outapp->pBuffer; ds2->duel_initiator = entity->GetID(); ds2->duel_target = entity->GetID(); @@ -3347,485 +5366,218 @@ void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +void Client::Handle_OP_DumpName(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) - return; + return; +} - EQApplicationPacket* outapp = app->Copy(); - Duel_Struct* ds = (Duel_Struct*) outapp->pBuffer; - uint32 duel = ds->duel_initiator; - ds->duel_initiator = ds->duel_target; - ds->duel_target = duel; - Entity* entity = entity_list.GetID(ds->duel_target); - if(GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { - Message_StringID(10,DUEL_CONSIDERING,entity->GetName()); - return; +void Client::Handle_OP_Dye(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DyeStruct)) + printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n", app->size, sizeof(DyeStruct)); + else{ + DyeStruct* dye = (DyeStruct*)app->pBuffer; + DyeArmor(dye); } - if(IsDueling()) { - Message_StringID(10,DUEL_INPROGRESS); + return; +} + +void Client::Handle_OP_Emote(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Emote_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Emote: got %d, expected %d", app->size, + sizeof(Emote_Struct)); + DumpPacket(app); return; } - if(GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { - SetDuelTarget(ds->duel_target); - entity->CastToClient()->SetDuelTarget(GetID()); - ds->duel_target = ds->duel_initiator; - entity->CastToClient()->FastQueuePacket(&outapp); - entity->CastToClient()->SetDueling(false); - SetDueling(false); + // Calculate new packet dimensions + Emote_Struct* in = (Emote_Struct*)app->pBuffer; + in->message[1023] = '\0'; + + const char* name = GetName(); + uint32 len_name = strlen(name); + uint32 len_msg = strlen(in->message); + // crash protection -- cheater + if (len_msg > 512) { + in->message[512] = '\0'; + len_msg = 512; + } + uint32 len_packet = sizeof(in->unknown01) + len_name + + len_msg + 1; + + // Construct outgoing packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); + Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; + out->unknown01 = in->unknown01; + memcpy(out->message, name, len_name); + memcpy(&out->message[len_name], in->message, len_msg); + + /* + if (target && target->IsClient()) { + entity_list.QueueCloseClients(this, outapp, false, 100, target); + + cptr = outapp->pBuffer + 2; + + // not sure if live does this or not. thought it was a nice feature, but would take a lot to + // clean up grammatical and other errors. Maybe with a regex parser... + replacestr((char *)cptr, target->GetName(), "you"); + replacestr((char *)cptr, " he", " you"); + replacestr((char *)cptr, " she", " you"); + replacestr((char *)cptr, " him", " you"); + replacestr((char *)cptr, " her", " you"); + target->CastToClient()->QueuePacket(outapp); + } else - safe_delete(outapp); + */ + entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); + + safe_delete(outapp); return; } -void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) { - if (app->size != sizeof(SpawnAppearance_Struct)) { - std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; return; } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - if(sa->spawn_id != GetID()) - return; + SetLooting(false); - if (sa->type == AT_Invis) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - if(GetClientVersion() < EQClientSoF) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - } - return; - } - invisible = false; - hidden = false; - improved_hidden = false; - entity_list.QueueClients(this, app, true); + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); + if (GetClientVersion() >= EQClientSoD) + Corpse::SendEndLootErrorPacket(this); + else + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_Anim) { - if (IsAIControlled()) - return; - if (sa->parameter == ANIM_STAND) { - SetAppearance(eaStanding); - playeraction = 0; - SetFeigned(false); - BindWound(this, false, true); - camp_timer.Disable(); - } - else if (sa->parameter == ANIM_SIT) { - SetAppearance(eaSitting); - playeraction = 1; - if(!UseBardSpellLogic()) - InterruptSpell(); - SetFeigned(false); - BindWound(this, false, true); - } - else if (sa->parameter == ANIM_CROUCH) { - if(!UseBardSpellLogic()) - InterruptSpell(); - SetAppearance(eaCrouching); - playeraction = 2; - SetFeigned(false); - } - else if (sa->parameter == ANIM_DEATH) { // feign death too - SetAppearance(eaDead); - playeraction = 3; - InterruptSpell(); - } - else if (sa->parameter == ANIM_LOOT) { - SetAppearance(eaLooting); - playeraction = 4; - SetFeigned(false); - } - - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ - else { - std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; - return; - } - - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Anon) { - // For Anon/Roleplay - if (sa->parameter == 1) { // Anon - m_pp.anon = 1; - } - else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp - m_pp.anon = 2; - } - else if (sa->parameter == 0) { // This is Non-Anon - m_pp.anon = 0; - } - else { - std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; - return; - } - entity_list.QueueClients(this, app, true); - UpdateWho(); - } - else if ((sa->type == AT_HP) && (dead == 0)) { + else if (!entity->IsCorpse()) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Split) { - m_pp.autosplit = (sa->parameter == 1); - } - else if (sa->type == AT_Sneak) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillSneak)) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - return; - } - this->sneaking = 0; - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Size) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) - { - entity_list.QueueClients(this, app, false); - } - else if (sa->type == AT_Levitate) - { - // don't do anything with this, we tell the client when it's - // levitating, not the other way around - } - else if (sa->type == AT_ShowHelm) - { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } else { - std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec - << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + entity->CastToCorpse()->EndLoot(this, app); } return; } -void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) { - if (app->size != sizeof(BazaarInspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", - sizeof(BazaarInspect_Struct), app->size); + if (!ClientFinishedLoading()) + { + SetHP(GetHP() - 1); return; } - BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bis->ItemID); - - if (!item) { - Message(13, "Error: This item does not exist!"); - return; - } - - ItemInst* inst = database.CreateItem(item); - - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - - return; -} - -void Client::Handle_OP_Death(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Death_Struct)) - return; - - Death_Struct* ds = (Death_Struct*)app->pBuffer; - - //I think this attack_skill value is really a value from SkillDamageTypes... - if(ds->attack_skill > HIGHEST_SKILL) { - mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); - return; - } - - if(GetHP() > 0) - return; - - Mob* killer = entity_list.GetMob(ds->killer_id); - Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); - return; -} - -void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MoveCoin_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + if (app->size != sizeof(EnvDamage2_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, + sizeof(EnvDamage2_Struct)); DumpPacket(app); return; } - OPMoveCoin(app); - return; -} - -void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(ItemViewRequest_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); - DumpPacket(app); + EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; + if (admin >= minStatusToAvoidFalling && GetGM()){ + Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge + return; + } + else if (GetInvul()) { + Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge return; } - DumpPacket(app); - ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + int damage = ed->damage; - //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + if (ed->dmgtype == 252) { - const Item_Struct* item = database.GetItem(ivrs->item_id); - if (!item) { - if (ivrs->item_id > 500000) - { - std::string response = ""; - int sayid = ivrs->item_id - 500000; - bool silentsaylink = false; - - if (sayid > 250000) //Silent Saylink - { - sayid = sayid - 250000; - silentsaylink = true; - } - - if (sayid > 0) - { - - std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - if (results.RowCount() != 1) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - auto row = results.begin(); - response = row[0]; - - } - - if((response).size() > 0) - { - if( !mod_saylink(response, silentsaylink) ) { return; } - - if(GetTarget() && GetTarget()->IsNPC()) - { - if(silentsaylink) - { - parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - else - { - if(silentsaylink) - { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - } - else - { - Message(13, "Error: Say Link not found or is too long."); - return; - } + switch (GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then + case 1: + damage = damage * 95 / 100; + break; + case 2: + damage = damage * 90 / 100; + break; + case 3: + damage = damage * 80 / 100; + break; } - else { - Message(13, "Error: The item for the link you have clicked on does not exist!"); - return; - } - } - ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + if (damage < 0) + damage = 31337; -void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) { - if (app->size != sizeof(LDONItemViewRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) return; - } - LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; - ItemInst* inst = database.CreateItem(item->item_id); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + else + SetHP(GetHP() - damage); -void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) -{ - if(!CharacterID()) + if (GetHP() <= 0) { - return; + mod_client_death_env(); + + Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); } - - if (app->size != sizeof(MoveItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); - return; - } - - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if(mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - char *detect = nullptr; - const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); - const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); - MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", - mi->from_slot, - itm_from ? itm_from->GetID() : 0, - mi->to_slot, - itm_to ? itm_to->GetID() : 0, - casting_spell_id); - database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); - safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots - return; - } - } - - // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if(mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if(!m_inv[from_parent]) { mi_hack = true; } - else if(!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if(!m_inv[to_parent]) { mi_hack = true; } - else if(!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } - - if(!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } - + SendHPUpdate(); return; } -void Client::Handle_OP_Camp(const EQApplicationPacket *app) { -#ifdef BOTS - // This block is necessary to clean up any bot objects owned by a Client - Bot::BotHealRotationsClear(this); - Bot::BotOrderCampAll(this); -#endif - if(IsLFP()) - worldserver.StopLFP(CharacterID()); - - if (GetGM()) - { - OnDisconnect(true); - return; - } - camp_timer.Start(29000,true); - return; -} - -void Client::Handle_OP_Logout(const EQApplicationPacket *app) +void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) { - //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); - //we will save when we get destroyed soon anyhow - //Save(); + if (app->size != sizeof(FaceChange_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", + sizeof(FaceChange_Struct), app->size); + return; + } - SendLogoutPackets(); + // Notify other clients in zone + entity_list.QueueClients(this, app, false); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - Disconnect(); + FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; + m_pp.haircolor = fc->haircolor; + m_pp.beardcolor = fc->beardcolor; + m_pp.eyecolor1 = fc->eyecolor1; + m_pp.eyecolor2 = fc->eyecolor2; + m_pp.hairstyle = fc->hairstyle; + m_pp.face = fc->face; + m_pp.beard = fc->beard; + m_pp.drakkin_heritage = fc->drakkin_heritage; + m_pp.drakkin_tattoo = fc->drakkin_tattoo; + m_pp.drakkin_details = fc->drakkin_details; + Save(); + Message_StringID(13, FACE_ACCEPTED); + //Message(13, "Facial features updated."); return; } void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) { - if(GetClass() != MONK) + if (GetClass() != MONK) return; - if(!p_timers.Expired(&database, pTimerFeignDeath, false)) { - Message(13,"Ability recovery time not yet met."); + if (!p_timers.Expired(&database, pTimerFeignDeath, false)) { + Message(13, "Ability recovery time not yet met."); return; } int reuse = FeignDeathReuseTime; switch (GetAA(aaRapidFeign)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 2; - break; - case 3: - reuse -= 5; - break; + case 1: + reuse -= 1; + break; + case 2: + reuse -= 2; + break; + case 3: + reuse -= 5; + break; } - p_timers.Start(pTimerFeignDeath, reuse-1); + p_timers.Start(pTimerFeignDeath, reuse - 1); //BreakInvis(); @@ -3852,213 +5604,663 @@ void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) return; } -void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) { - if(!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { - return; //You cannot sneak if you do not have sneak - } - - if(!p_timers.Expired(&database, pTimerSneak, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerSneak, SneakReuseTime-1); - - bool was = sneaking; - if (sneaking){ - sneaking = false; - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } + if (app->size != sizeof(FindPersonRequest_Struct)) + printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); else { - CheckIncreaseSkill(SkillSneak, nullptr, 5); - } - float hidechance = ((GetSkill(SkillSneak)/300.0f) + .25) * 100; - float random = MakeRandomFloat(0, 99); - if(!was && random < hidechance) { - sneaking = true; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x0F; - sa_out->parameter = sneaking; - QueuePacket(outapp); - safe_delete(outapp); - if(GetClass() == ROGUE){ - outapp = new EQApplicationPacket(OP_SimpleMessage,12); - SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer; - msg->color=0x010E; - if (sneaking){ - msg->string_id=347; + FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; + + std::vector points; + Mob* target = entity_list.GetMob(t->npc_id); + + if (target == nullptr) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; } - else { - msg->string_id=348; + + if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || + target->CastToClient()->Buyer)) { + Message(15, "Moving you to Trader %s", target->GetName()); + MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); } - FastQueuePacket(&outapp); - } - return; -} -void Client::Handle_OP_Hide(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - //Can not be able to train hide but still have it from racial though - return; //You cannot hide if you do not have hide - } - - if(!p_timers.Expired(&database, pTimerHide, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = HideReuseTime - GetAA(209); - p_timers.Start(pTimerHide, reuse-1); - - float hidechance = ((GetSkill(SkillHide)/250.0f) + .25) * 100; - float random = MakeRandomFloat(0, 100); - CheckIncreaseSkill(SkillHide, nullptr, 5); - if (random < hidechance) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 1; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetAA(aaShroudofStealth)){ - improved_hidden = true; - hidden = true; + if (!RuleB(Pathing, Find) || !zone->pathing) + { + //fill in the path array... + // + points.resize(2); + points[0].x = GetX(); + points[0].y = GetY(); + points[0].z = GetZ(); + points[1].x = target->GetX(); + points[1].y = target->GetY(); + points[1].z = target->GetZ(); } else - hidden = true; - } - if(GetClass() == ROGUE){ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct)); - SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; - msg->color = 0x010E; - Mob *evadetar = GetTarget(); - if (!auto_attack && (evadetar && evadetar->CheckAggro(this) - && evadetar->IsNPC())) { - if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { - msg->string_id = EVADE_SUCCESS; - RogueEvade(evadetar); - } else { - msg->string_id = EVADE_FAIL; + { + Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); + Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); + + if (!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) + { + points.resize(2); + points[0].x = Start.x; + points[0].y = Start.y; + points[0].z = Start.z; + + points[1].x = End.x; + points[1].y = End.y; + points[1].z = End.z; + } - } else { - if (hidden){ - msg->string_id = HIDE_SUCCESS; - } - else { - msg->string_id = HIDE_FAIL; + else + { + std::list pathlist = zone->pathing->FindRoute(Start, End); + + if (pathlist.size() == 0) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + //the client seems to have issues with packets larger than this + if (pathlist.size() > 36) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + // Live appears to send the points in this order: + // Final destination. + // Current Position. + // rest of the points. + FindPerson_Point p; + + int PointNumber = 0; + + bool LeadsToTeleporter = false; + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); + + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + + p.x = GetX(); + p.y = GetY(); + p.z = GetZ(); + points.push_back(p); + + for (std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) + { + if ((*Iterator) == -1) // Teleporter + { + LeadsToTeleporter = true; + break; + } + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + ++PointNumber; + } + + if (!LeadsToTeleporter) + { + p.x = target->GetX(); + p.y = target->GetY(); + p.z = target->GetZ(); + + points.push_back(p); + } + } } - FastQueuePacket(&outapp); + + SendPathPacket(points); } return; } -void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +void Client::Handle_OP_Fishing(const EQApplicationPacket *app) { - ChannelMessage_Struct* cm=(ChannelMessage_Struct*)app->pBuffer; - - if (app->size < sizeof(ChannelMessage_Struct)) { - std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; - return; - } - if (IsAIControlled()) { - Message(13, "You try to speak but cant move your mouth!"); + if (!p_timers.Expired(&database, pTimerFishing, false)) { + Message(13, "Ability recovery time not yet met."); return; } - ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + if (CanFish()) { + parse->EventPlayer(EVENT_FISH_START, this, "", 0); + + //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) + p_timers.Start(pTimerFishing, FishingReuseTime - 1); + fishing_timer.Start(); + } return; + // Changes made based on Bobs work on foraging. Now can set items in the forage database table to + // forage for. } -void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +void Client::Handle_OP_Forage(const EQApplicationPacket *app) { - if (app->size != sizeof(WearChange_Struct)) { - std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + + if (!p_timers.Expired(&database, pTimerForaging, false)) { + Message(13, "Ability recovery time not yet met."); return; } + p_timers.Start(pTimerForaging, ForagingReuseTime - 1); - WearChange_Struct* wc=(WearChange_Struct*)app->pBuffer; - if(wc->spawn_id != GetID()) - return; + ForageItem(); - // we could maybe ignore this and just send our own from moveitem - entity_list.QueueClients(this, app, true); - return; -} - -//in zoning.cpp -//void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { -//} - -void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) -{ - // The client will send this with his id when he zones, maybe when he disconnects too? - //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning - - //just make sure this gets out - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); - EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; - eid->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - hate_list.RemoveEnt(this->CastToMob()); - - Disconnect(); - return; -} - -void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) -{ - Handle_OP_Save(app); -} - -void Client::Handle_OP_Save(const EQApplicationPacket *app) -{ - // The payload is 192 bytes - Not sure what is contained in payload - Save(); - return; -} - -void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Who_All_Struct)) { - std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; - return; - } - Who_All_Struct* whoall = (Who_All_Struct*) app->pBuffer; - - if(whoall->type == 0) // SoF only, for regular /who - entity_list.ZoneWho(this, whoall); - else - WhoAll(whoall); return; } void Client::Handle_OP_FriendsWho(const EQApplicationPacket *app) { - char *FriendsString = (char*) app->pBuffer; + char *FriendsString = (char*)app->pBuffer; FriendsWho(FriendsString); return; } +void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildMOTD(true); + + if (IsInAGuild()) + { + SendGuildURL(); + SendGuildChannel(); + } +} + +void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildList(); +} + +void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); + return; + } + if (app->size != sizeof(BecomeNPC_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); + return; + } + //entity_list.QueueClients(this, app, false); + BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; + + Mob* cli = (Mob*)entity_list.GetMob(bnpc->id); + if (cli == 0) + return; + + if (cli->IsClient()) + cli->CastToClient()->QueuePacket(app); + cli->SendAppearancePacket(AT_NPCName, 1, true); + cli->CastToClient()->SetBecomeNPC(true); + cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); + cli->Message_StringID(0, TOGGLE_OFF); + cli->CastToClient()->tellsoff = true; + //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. + return; +} + +void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMDelCorpse_Struct)) + return; + if (this->Admin() < commandEditPlayerCorpses) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); + return; + } + GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; + Mob* corpse = entity_list.GetMob(dc->corpsename); + if (corpse == 0) { + return; + } + if (corpse->IsCorpse() != true) { + return; + } + corpse->CastToCorpse()->Delete(); + std::cout << name << " deleted corpse " << dc->corpsename << std::endl; + Message(13, "Corpse %s deleted.", dc->corpsename); + return; +} + +void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/emote"); + return; + } + if (app->size != sizeof(GMEmoteZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); + return; + } + GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; + char* newmessage = 0; + if (strstr(gmez->text, "^") == 0) + entity_list.Message(0, 15, gmez->text); + else{ + for (newmessage = strtok((char*)gmez->text, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) + entity_list.Message(0, 15, newmessage); + } + return; +} + +void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainEnd_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); + DumpPacket(app); + return; + } + OPGMEndTraining(app); + return; +} + +void Client::Handle_OP_GMFind(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/find"); + return; + } + if (app->size != sizeof(GMSummon_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); + return; + } + //Break down incoming + GMSummon_Struct* request = (GMSummon_Struct*)app->pBuffer; + //Create a new outgoing + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); + GMSummon_Struct* foundplayer = (GMSummon_Struct*)outapp->pBuffer; + //Copy the constants + strcpy(foundplayer->charname, request->charname); + strcpy(foundplayer->gmname, request->gmname); + //Check if the NPC exits intrazone... + Mob* gt = entity_list.GetMob(request->charname); + if (gt != 0) { + foundplayer->success = 1; + foundplayer->x = (int32)gt->GetX(); + foundplayer->y = (int32)gt->GetY(); + + foundplayer->z = (int32)gt->GetZ(); + foundplayer->zoneID = zone->GetZoneID(); + } + //Send the packet... + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/goto"); + return; + } + GMSummon_Struct* gmg = (GMSummon_Struct*)app->pBuffer; + Mob* gt = entity_list.GetMob(gmg->charname); + if (gt != nullptr) { + this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); + } + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected."); + else { + ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); + memset(pack->pBuffer, 0, pack->size); + ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*)pack->pBuffer; + strcpy(wsgmg->myname, this->GetName()); + strcpy(wsgmg->gotoname, gmg->charname); + wsgmg->admin = admin; + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/hideme"); + return; + } + if (app->size != sizeof(SpawnAppearance_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + Message(13, "#: %i, %i", sa->type, sa->parameter); + SetHideMe(!sa->parameter); + return; + +} + +void Client::Handle_OP_GMKick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMKick_Struct)) + return; + if (this->Admin() < minStatusToKick) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kick"); + return; + } + GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; + + Client* client = entity_list.GetClientByName(gmk->name); + if (client == 0) { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack->pBuffer; + strcpy(skp->adminname, gmk->gmname); + strcpy(skp->name, gmk->name); + skp->adminrank = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + else { + entity_list.QueueClients(this, app); + //client->Kick(); + } + return; +} + +void Client::Handle_OP_GMKill(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kill"); + return; + } + if (app->size != sizeof(GMKill_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); + return; + } + GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; + Mob* obj = entity_list.GetMob(gmk->name); + Client* client = entity_list.GetClientByName(gmk->name); + if (obj != 0) { + if (client != 0) { + entity_list.QueueClients(this, app); + } + else { + obj->Kill(); + } + } + else { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); + ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*)pack->pBuffer; + strcpy(skp->gmname, gmk->gmname); + strcpy(skp->target, gmk->name); + skp->admin = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + return; +} + +void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMLastName_Struct)) { + std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; + return; + } + GMLastName_Struct* gmln = (GMLastName_Struct*)app->pBuffer; + if (strlen(gmln->lastname) >= 64) { + Message(13, "/LastName: New last name too long. (max=63)"); + } + else { + Client* client = entity_list.GetClientByName(gmln->name); + if (client == 0) { + Message(13, "/LastName: %s not found", gmln->name); + } + else { + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(client->account_name, client->name, "/lastname"); + return; + } + else + + client->ChangeLastName(gmln->lastname); + } + gmln->unknown[0] = 1; + gmln->unknown[1] = 1; + gmln->unknown[2] = 1; + gmln->unknown[3] = 1; + entity_list.QueueClients(this, app, false); + } + return; +} + +void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMName_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); + return; + } + const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; + if (this->Admin() < minStatusToUseGMCommands){ + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/name"); + return; + } + Client* client = entity_list.GetClientByName(gmn->oldname); + LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); + bool usedname = database.CheckUsedName((const char*)gmn->newname); + if (client == 0) { + Message(13, "%s not found for name change. Operation failed!", gmn->oldname); + return; + } + if ((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { + Message(13, "Invalid number of characters in new name (%s).", gmn->newname); + return; + } + if (!usedname) { + Message(13, "%s is already in use. Operation failed!", gmn->newname); + return; + + } + database.UpdateName(gmn->oldname, gmn->newname); + strcpy(client->name, gmn->newname); + client->Save(); + + if (gmn->badname == 1) { + database.AddToNameFilter(gmn->oldname); + } + EQApplicationPacket* outapp = app->Copy(); + GMName_Struct* gmn2 = (GMName_Struct*)outapp->pBuffer; + gmn2->unknown[0] = 1; + gmn2->unknown[1] = 1; + gmn2->unknown[2] = 1; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + UpdateWho(); + return; +} + +void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) +{ + // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can + // be displayed in the window, including all the HTML formatting tags. + // + const int maxResults = 10; + + if (app->size < sizeof(GMSearchCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", + app->size, sizeof(GMSearchCorpse_Struct)); + DumpPacket(app); + return; + } + + GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; + gmscs->Name[63] = '\0'; + + char *escSearchString = new char[129]; + database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); + + std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " + "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", + escSearchString, maxResults); + safe_delete_array(escSearchString); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); + return; + } + + if (results.RowCount() == 0) + return; + + if (results.RowCount() == maxResults) + Message(clientMessageError, "Your search found too many results; some are not displayed."); + else + Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); + + char charName[64], timeOfDeath[20]; + + std::string popupText = ""; + + for (auto row = results.begin(); row != results.end(); ++row) { + + strn0cpy(charName, row[0], sizeof(charName)); + + uint32 ZoneID = atoi(row[1]); + float CorpseX = atof(row[2]); + float CorpseY = atof(row[3]); + float CorpseZ = atof(row[4]); + + strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); + + bool corpseRezzed = atoi(row[6]); + bool corpseBuried = atoi(row[7]); + + popupText += StringFormat("", + charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, + corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); + + if (popupText.size() > 4000) { + Message(clientMessageError, "Unable to display all the results."); + break; + } + + } + + popupText += "
NameZoneXYZDate" + "RezzedBuried
 " + "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; + + SendPopupToClient("Corpses", popupText.c_str()); + +} + +void Client::Handle_OP_GMServers(const EQApplicationPacket *app) +{ + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName()) + 2); + memset(pack->pBuffer, (uint8)admin, 1); + strcpy((char *)&pack->pBuffer[1], this->GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + OPGMSummon(app); + return; +} + +void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMToggle_Struct)) { + std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/toggle"); + return; + } + GMToggle_Struct *ts = (GMToggle_Struct *)app->pBuffer; + if (ts->toggle == 0) { + this->Message_StringID(0, TOGGLE_OFF); + //Message(0, "Turning tells OFF"); + tellsoff = true; + } + else if (ts->toggle == 1) { + //Message(0, "Turning tells ON"); + this->Message_StringID(0, TOGGLE_ON); + tellsoff = false; + } + else { + Message(0, "Unkown value in /toggle packet"); + } + UpdateWho(); + return; +} + +void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainee_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTraining(app); + return; +} + +void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSkillChange_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTrainSkill(app); + return; +} + void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) { if (app->size != sizeof(GMZoneRequest_Struct)) { @@ -4081,7 +6283,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) if (gmzr->zone_id == 0) zid = zonesummon_id; const char * zname = database.GetZoneName(zid); - if(zname == nullptr) + if (zname == nullptr) tarzone[0] = 0; else strcpy(tarzone, zname); @@ -4092,7 +6294,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) } EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); - GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*) outapp->pBuffer; + GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*)outapp->pBuffer; strcpy(gmzr2->charname, this->GetName()); gmzr2->zone_id = gmzr->zone_id; gmzr2->x = tarx; @@ -4128,2895 +6330,6 @@ void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(false); - - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); - if(GetClientVersion() >= EQClientSoD) - Corpse::SendEndLootErrorPacket(this); - else - Corpse::SendLootReqErrorPacket(this); - return; - } - else if (!entity->IsCorpse()) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); - Corpse::SendLootReqErrorPacket(this); - return; - } - else { - entity->CastToCorpse()->EndLoot(this, app); - } - return; -} - -void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(true); - - Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); - if (ent == 0) { - Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); - Corpse::SendLootReqErrorPacket(this); - return; - } - if (ent->IsCorpse()) - { - Corpse *ent_corpse = ent->CastToCorpse(); - if(DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) - { - Message(13, "Corpse too far away."); - Corpse::SendLootReqErrorPacket(this); - return; - } - - if(invisible) { - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - if(hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - ent->CastToCorpse()->MakeLootRequestPackets(this, app); - return; - } - else { - std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; - Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); - Corpse::SendLootReqErrorPacket(this); - } - return; -} - -void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->IsNPC()) - HandleLDoNOpen(target->CastToNPC()); -} - -void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillSenseTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the sense traps skill."); - } -} - -void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillDisarmTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the disarm trap skill."); - } -} - -void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillPickLock)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); - } - else - Message(13, "You do not have the pick locks skill."); - } -} - -void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->GetClass() == LDON_TREASURE) - Message(15, "%s", target->GetCleanName()); -} - -void Client::Handle_OP_Dye(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(DyeStruct)) - printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n",app->size,sizeof(DyeStruct)); - else{ - DyeStruct* dye = (DyeStruct*)app->pBuffer; - DyeArmor(dye); - } - return; -} - -void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app){ - return; -} - -void Client::Handle_OP_LootItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LootingItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); - return; - } - /* - ** fixed the looting code so that it sends the correct opcodes - ** and now correctly removes the looted item the player selected - ** as well as gives the player the proper item. - ** Also fixed a few UI lock ups that would occur. - */ - - EQApplicationPacket* outapp = 0; - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); - outapp = new EQApplicationPacket(OP_LootComplete, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - if (entity->IsCorpse()) { - entity->CastToCorpse()->LootItem(this, app); - return; - } - else { - Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); - Corpse::SendEndLootErrorPacket(this); - } - - return; -} - -void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - Message(0,"You are not a guild leader or not in a guild."); - else { - mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); - if (!guild_mgr.DeleteGuild(GuildID())) - Message(0, "Guild delete failed."); - else { - Message(0, "Guild successfully deleted."); - } - } -} - -void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < sizeof(GuildUpdate_PublicNote)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n",app->size,sizeof(GuildUpdate_PublicNote)); - return; - } - GuildUpdate_PublicNote* gpn=(GuildUpdate_PublicNote*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gpn->target, gci)) { - Message(0, "Unable to find '%s'", gpn->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", - gpn->target, gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID(), - gpn->note); - - if(!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { - Message(13, "Failed to set public note on %s", gpn->target); - } else { - Message(0, "Successfully changed public note on %s", gpn->target); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildMOTD(true); - - if(IsInAGuild()) - { - SendGuildURL(); - SendGuildChannel(); - } -} - -void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildList(); -} - -void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildMOTD_Struct)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n",app->size,sizeof(GuildMOTD_Struct)); - return; - } - if(!IsInAGuild()) { - Message(13, "You are not in a guild!"); - return; - } - if(!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { - Message(13, "You do not have permissions to edit your guild's MOTD."); - return; - } - - GuildMOTD_Struct* gmotd=(GuildMOTD_Struct*)app->pBuffer; - - mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", - guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); - - if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { - Message(0, "Motd update failed."); - } - - return; -} - -void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) -{ - - mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - if(app->size != sizeof(GuildManageBanker_Struct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); - return; - } - GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*) app->pBuffer; - - if(!IsInAGuild()) { - Message(13, "Your not in a guild!"); - return; - } - - CharGuildInfo gci; - - if(!guild_mgr.GetCharInfo(gmb->member, gci)) - { - Message(0, "Unable to find '%s'", gmb->member); - return; - } - bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); - - bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); - - bool NewBankerStatus = gmb->enabled & 0x01; - - bool NewAltStatus = gmb->enabled & 0x02; - - if((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can assign guild bankers!"); - return; - } - - if(IsCurrentlyAnAlt != NewAltStatus) - { - bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); - - if(!IsAllowed) - { - Message(13, "You are not allowed to change the alt status of %s", gmb->member); - return; - } - } - - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(IsCurrentlyABanker != NewBankerStatus) - { - if(!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { - Message(13, "Error setting guild banker flag."); - return; - } - - if(NewBankerStatus) - Message(0, "%s has been made a guild banker.", gmb->member); - else - Message(0, "%s is no longer a guild banker.", gmb->member); - } - if(IsCurrentlyAnAlt != NewAltStatus) - { - if(!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { - Message(13, "Error setting guild alt flag."); - return; - } - - if(NewAltStatus) - Message(0, "%s has been marked as an alt.", gmb->member); - else - Message(0, "%s is no longer marked as an alt.", gmb->member); - } -} - -void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < 2) { - mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); - return; - } - - app->pBuffer[app->size-1] = 0; - GuildMakeLeader* gml=(GuildMakeLeader*)app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (GuildRank() != GUILD_LEADER) - Message(0, "Error: You arent the guild leader!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //NOTE: we could do cross-zone lookups here... - - Client* newleader = entity_list.GetClientByName(gml->target); - if(newleader) { - - mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", - guild_mgr.GetGuildName(GuildID()), GuildID(), - newleader->GetName(), newleader->CharacterID()); - - if(guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ - Message(0,"Successfully Transfered Leadership to %s.",gml->target); - newleader->Message(15,"%s has transfered the guild leadership into your hands.",GetName()); - } - else - Message(0,"Could not change leadership at this time."); - } - else - Message(0,"Failed to change leader, could not find target."); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildDemoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildDemoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(demote->target, gci)) { - Message(0, "Unable to find '%s'", demote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(gci.rank < 1) { - Message(0, "%s cannot be demoted any further!", demote->target); - return; - } - uint8 rank = gci.rank - 1; - - - mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - demote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); - return; - } - Message(0, "Successfully demoted %s to rank %d", demote->target, rank); - } -// SendGuildMembers(GuildID(), true); - return; -} - - -void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildPromoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildPromoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(promote->target, gci)) { - Message(0, "Unable to find '%s'", promote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - uint8 rank = gci.rank + 1; - - if(rank > GUILD_OFFICER) - return; - - - mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - promote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); - return; - } - Message(0, "Successfully promoted %s to rank %d", promote->target, rank); - } - return; -} - -void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - - if (!IsInAGuild()) - Message(0, "Error: You are not in a guild!"); - else if(gc->officer > GUILD_MAX_RANK) - Message(13, "Invalid rank."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //ok, the invite is also used for changing rank as well. - Mob* invitee = entity_list.GetMob(gc->othername); - - if(!invitee) { - Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); - return; - } - - if(invitee->IsClient()) { - Client* client = invitee->CastToClient(); - - //ok, figure out what they are trying to do. - if(client->GuildID() == GuildID()) { - //they are already in this guild, must be a promotion or demotion - if(gc->officer < client->GuildRank()) { - //demotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - //we could send this to the member and prompt them to see if they want to - //be demoted (I guess), but I dont see a point in that. - - mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { - Message(13, "There was an error during the demotion, DB may now be inconsistent."); - return; - } - - } else if(gc->officer > client->GuildRank()) { - //promotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the promotion with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->QueuePacket(app); - - } else { - Message(13, "That member is already that rank."); - return; - } - } else if(!client->IsInAGuild()) { - //they are not in this or any other guild, this is an invite - // - if(client->GetPendingGuildInvitation()) - { - Message(13, "That person is already considering a guild invitation."); - return; - } - - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { - Message(13, "You dont have permission to invite."); - return; - } - - mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the invite with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->SetPendingGuildInvitation(true); - client->QueuePacket(app); - - } else { - //they are in some other guild - Message(13,"Player is in a guild."); - return; - } - } -#ifdef BOTS - else if (invitee->IsBot()) { - // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system - Bot::ProcessGuildInvite(this, invitee->CastToBot()); - return; - } -#endif - } -} - -void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - // we can always remove ourself, otherwise, our rank needs remove permissions - else if (strcasecmp(gc->othername,GetName()) != 0 && - !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) - Message(0, "You dont have permission to remove guild members."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { -#ifdef BOTS - if(Bot::ProcessGuildRemoval(this, gc->othername)) - return; -#endif - uint32 char_id; - Client* client = entity_list.GetClientByName(gc->othername); - - if(client) { - if(!client->IsInGuild(GuildID())) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = client->CharacterID(); - - mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - } else { - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gc->othername, gci)) { - Message(0, "Unable to find '%s'", gc->othername); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = gci.char_id; - - mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", - gci.char_name.c_str(), gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID()); - } - - if(!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); - GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*) outapp->pBuffer; - gm->guildeqid = GuildID(); - strcpy(gm->member, gc->othername); - Message(0,"%s successfully removed from your guild.",gc->othername); - entity_list.QueueClientsGuild(this, outapp, false, GuildID()); - safe_delete(outapp); - } - else - Message(0,"Unable to remove %s from your guild.",gc->othername); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SetPendingGuildInvitation(false); - - if (app->size != sizeof(GuildInviteAccept_Struct)) { - std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; - return; - } - - GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*) app->pBuffer; - - if(GetClientVersion() >= EQClientRoF) - { - if(gj->response > 9) - { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - return; - } - } - if (gj->response == 5 || gj->response == 4) { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - - return; - } - - //uint32 tmpeq = gj->guildeqid; - if (IsInAGuild() && gj->response==GuildRank()) - Message(0, "Error: You're already in a guild!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", - gj->guildeqid, gj->response, gj->inviter, gj->newmember); - - //we dont really care a lot about what this packet means, as long as - //it has been authorized with the guild manager - if(!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); - Message(13, "Invalid invite response packet!"); - return; - } - - if(gj->guildeqid == GuildID()) { - //only need to change rank. - - mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - gj->response, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { - Message(13, "There was an error during the rank change, DB may now be inconsistent."); - return; - } - } else { - - mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", - GetName(), CharacterID(), - guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, - gj->response); - - //change guild and rank - - uint32 guildrank = gj->response; - - if(GetClientVersion() == EQClientRoF) - { - if(gj->response == 8) - { - guildrank = 0; - } - } - - if(!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { - Message(13, "There was an error during the invite, DB may now be inconsistent."); - return; - } - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - } - } -} - -void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) -{ - if(app->size == 0) { - // i think thats the sign to stop the songs - if(IsBardSong(casting_spell_id) || bardsong != 0) - InterruptSpell(SONG_ENDS, 0x121); - else - InterruptSpell(INTERRUPT_SPELL, 0x121); - - return; - } - else // I don't think the client sends proper manachanges - { // with a length, just the 0 len ones for stopping songs - //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; - printf("OP_ManaChange from client:\n"); - DumpPacket(app); - } - return; -} - -void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) -{ - OPMemorizeSpell(app); - return; -} - -void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(SwapSpell_Struct)) { - std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; - return; - } - const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*) app->pBuffer; - int swapspelltemp; - - if(swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) - return; - - swapspelltemp = m_pp.spell_book[swapspell->from_slot]; - if (swapspelltemp < 0){ - return; - } - m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; - m_pp.spell_book[swapspell->to_slot] = swapspelltemp; - - database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); - database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); - - QueuePacket(app); - return; -} - -void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CastSpell_Struct)) { - std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; - return; - } - if (IsAIControlled()) { - this->Message_StringID(13, NOT_IN_CONTROL); - //Message(13, "You cant cast right now, you arent in control of yourself!"); - return; - } - - CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; - -#ifdef _EQDEBUG - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); -#endif - LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); - - std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; - - /* Memorized Spell */ - if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ - - uint16 spell_to_cast = 0; - if (castspell->slot < MAX_PP_MEMSPELL) { - spell_to_cast = m_pp.mem_spells[castspell->slot]; - if (spell_to_cast != castspell->spell_id) { - InterruptSpell(castspell->spell_id); //CHEATER!!! - return; - } - } - else if (castspell->slot >= MAX_PP_MEMSPELL) { - InterruptSpell(); - return; - } - - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - /* Spell Slot or Potion Belt Slot */ - else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast - { - //discipline, using the item spell slot - if (castspell->inventoryslot == 0xFFFFFFFF) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - } - return; - } - else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check - { - const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field - //bool cancast = true; - if (inst && inst->IsType(ItemClassCommon)) - { - const Item_Struct* item = inst->GetItem(); - if (item->Click.Effect != (uint32)castspell->spell_id) - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); - InterruptSpell(castspell->spell_id); //CHEATER!! - return; - } - - if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (item->Click.Level2 > 0) - { - if (GetLevel() >= item->Click.Level2) - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - else - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); - Message(0, "Error: level not high enough.", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - } - else - { - Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - /* Discipline */ - else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - return; - } - } - /* ABILITY cast (LoH and Harm Touch) */ - else if (castspell->slot == ABILITY_SPELL_SLOT) { - uint16 spell_to_cast = 0; - - if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { - if (!p_timers.Expired(&database, pTimerLayHands)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - spell_to_cast = SPELL_LAY_ON_HANDS; - p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); - } - else if ((castspell->spell_id == SPELL_HARM_TOUCH - || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { - if (!p_timers.Expired(&database, pTimerHarmTouch)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - - // determine which version of HT we are casting based on level - if (GetLevel() < 40) - spell_to_cast = SPELL_HARM_TOUCH; - else - spell_to_cast = SPELL_HARM_TOUCH2; - - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - - if (spell_to_cast > 0) // if we've matched LoH or HT, cast now - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - return; -} - -void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DeleteItem_Struct)) { - std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; - return; - } - - DeleteItem_Struct* alc = (DeleteItem_Struct*) app->pBuffer; - const ItemInst *inst = GetInv().GetItem(alc->from_slot); - if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { - entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); - CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); - - int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); - int16 IntoxicationIncrease; - - if(GetClientVersion() < EQClientSoD) - IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; - else - IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; - - if(IntoxicationIncrease < 0) - IntoxicationIncrease = 1; - - m_pp.intoxication += IntoxicationIncrease; - - if(m_pp.intoxication > 200) - m_pp.intoxication = 200; - } - DeleteItemInInventory(alc->from_slot, 1); - - return; -} - -void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CombatAbility_Struct)) { - std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; - return; - } - OPCombatAbility(app); - return; -} - -void Client::Handle_OP_Taunt(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: "<< sizeof(ClientTarget_Struct) << std::endl; - return; - } - - if(!p_timers.Expired(&database, pTimerTaunt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerTaunt, TauntReuseTime-1); - - if(GetTarget() == nullptr || !GetTarget()->IsNPC()) - return; - - Taunt(GetTarget()->CastToNPC(), false); - return; -} - -void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) -{ - //packet is empty as of 12/14/04 - - if(!p_timers.Expired(&database, pTimerInstillDoubt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime-1); - - InstillDoubt(GetTarget()); - return; -} - -void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); - - const Resurrect_Struct* ra = (const Resurrect_Struct*) app->pBuffer; - - _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", - PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); - - _pkt(SPELLS__REZ, app); - - OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); - - if(ra->action == 1) - { - EQApplicationPacket* outapp = app->Copy(); - // Send the OP_RezzComplete to the world server. This finds it's way to the zone that - // the rezzed corpse is in to mark the corpse as rezzed. - outapp->SetOpcode(OP_RezzComplete); - worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - OPGMSummon(app); - return; -} - -void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Client requesting a trade session from an npc/client - // Trade session not started until OP_TradeRequestAck is sent - - BreakInvis(); - - // Pass trade request on to recipient - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } -#ifndef BOTS - else if (tradee && tradee->IsNPC()) { -#else - else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { -#endif - //npcs always accept - trade->Start(msg->to_mob_id); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); - TradeRequest_Struct* acc = (TradeRequest_Struct*) outapp->pBuffer; - acc->from_mob_id = msg->to_mob_id; - acc->to_mob_id = msg->from_mob_id; - FastQueuePacket(&outapp); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Trade request recipient is acknowledging they are able to trade - // After this, the trade session has officially started - // Send ack on to trade initiator if client - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - trade->Start(msg->to_mob_id); - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CancelTrade_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); - return; - } - Mob* with = trade->With(); - if (with && with->IsClient()) { - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - - // Forward cancel packet to other client - msg->fromid = with->GetID(); - //msg->action = 1; - - with->CastToClient()->QueuePacket(app); - - // Put trade items/cash back into inventory - FinishTrade(this); - trade->Reset(); - } - else if(with){ - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - msg->fromid = with->GetID(); - QueuePacket(app); - FinishTrade(this); - trade->Reset(); - } - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) -{ - Mob* with = trade->With(); - trade->state = TradeAccepted; - - if (with && with->IsClient()) { - //finish trade... - // Have both accepted? - Client* other = with->CastToClient(); - other->QueuePacket(app); - - if (other->trade->state == trade->state) { - other->trade->state = TradeCompleting; - trade->state = TradeCompleting; - - // should we do this for NoDrop items as well? - if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { - Message_StringID(13, TRADE_CANCEL_LORE); - other->Message_StringID(13, TRADE_CANCEL_LORE); - this->FinishTrade(this); - other->FinishTrade(other); - other->trade->Reset(); - trade->Reset(); - } - else { - // Audit trade to database for both trade streams - other->trade->LogTrade(); - trade->LogTrade(); - - // start QS code - if(RuleB(QueryServ, PlayerLogTrades)) { - QSPlayerLogTrade_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); - - // Perform actual trade - this->FinishTrade(other, true, &event_entry, &event_details); - other->FinishTrade(this, false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); - QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSTradeItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - // end QS code - } - else { - this->FinishTrade(other); - other->FinishTrade(this); - } - - other->trade->Reset(); - trade->Reset(); - } - // All done - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - other->QueuePacket(outapp); - this->FastQueuePacket(&outapp); - } - } - // Trading with a Mob object that is not a Client. - else if(with) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - QueuePacket(outapp); - safe_delete(outapp); - if(with->IsNPC()) { - // Audit trade to database for player trade stream - if(RuleB(QueryServ, PlayerLogHandins)) { - QSPlayerLogHandin_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); - - FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); - QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSHandinItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - } - else { - FinishTrade(with->CastToNPC()); - } - } -#ifdef BOTS - // TODO: Log Bot trades - else if(with->IsBot()) - with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); -#endif - trade->Reset(); - } - - - return; -} - -void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeBusy_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); - return; - } - // Trade request recipient is cancelling the trade due to being busy - // Trade requester gets message "I'm busy right now" - // Send busy message on to trade initiator if client - TradeBusy_Struct* msg = (TradeBusy_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) -{ - - if(app->size <= 5) - return; - - char *boatname; - boatname = new char[app->size-3]; - memset(boatname, 0, app->size-3); - memcpy(boatname, app->pBuffer, app->size-4); - - Mob* boat = entity_list.GetMob(boatname); - if (boat) - this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat - safe_delete_array(boatname); - return; -} - -void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) -{ - Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id - if (boat) { - if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) - boat->SetTarget(0); // fix it to stop later problems - } - this->BoatID = 0; - return; -} - -void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RandomReq_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); - return; - } - const RandomReq_Struct* rndq = (const RandomReq_Struct*) app->pBuffer; - uint32 randLow=rndq->low > rndq->high?rndq->high:rndq->low; - uint32 randHigh=rndq->low > rndq->high?rndq->low:rndq->high; - uint32 randResult; - - if(randLow==0 && randHigh==0) - { // defaults - randLow=0; - randHigh=100; - } - randResult=MakeRandomInt(randLow, randHigh); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); - RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; - rr->low=randLow; - rr->high=randHigh; - rr->result=randResult; - strcpy(rr->name, GetName()); - entity_list.QueueCloseClients(this, outapp, false, 400); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Buff(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SpellBuffFade_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); - DumpPacket(app); - return; - } - - SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) app->pBuffer; - uint32 spid = sbf->spellid; - mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); - - //something about IsDetrimentalSpell() crashes this portion of code.. - //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and - //isdetrimentalspell() is much more complex - if(spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) - QueuePacket(app); - else - BuffFadeBySpellID(spid); - - return; -} - -void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/hideme"); - return; - } - if (app->size != sizeof(SpawnAppearance_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); - return; - } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - Message(13, "#: %i, %i", sa->type, sa->parameter); - SetHideMe(!sa->parameter); - return; - -} - -void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMName_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); - return; - } - const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; - if(this->Admin() < minStatusToUseGMCommands){ - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/name"); - return; - } - Client* client = entity_list.GetClientByName(gmn->oldname); - LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); - bool usedname = database.CheckUsedName((const char*) gmn->newname); - if(client==0) { - Message(13, "%s not found for name change. Operation failed!", gmn->oldname); - return; - } - if((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { - Message(13, "Invalid number of characters in new name (%s).", gmn->newname); - return; - } - if (!usedname) { - Message(13, "%s is already in use. Operation failed!", gmn->newname); - return; - - } - database.UpdateName(gmn->oldname, gmn->newname); - strcpy(client->name, gmn->newname); - client->Save(); - - if(gmn->badname==1) { - database.AddToNameFilter(gmn->oldname); - } - EQApplicationPacket* outapp = app->Copy(); - GMName_Struct* gmn2 = (GMName_Struct*) outapp->pBuffer; - gmn2->unknown[0] = 1; - gmn2->unknown[1] = 1; - gmn2->unknown[2] = 1; - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - UpdateWho(); - return; -} - -void Client::Handle_OP_GMKill(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kill"); - return; - } - if (app->size != sizeof(GMKill_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); - return; - } - GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; - Mob* obj = entity_list.GetMob(gmk->name); - Client* client = entity_list.GetClientByName(gmk->name); - if(obj!=0) { - if(client!=0) { - entity_list.QueueClients(this,app); - } - else { - obj->Kill(); - } - } - else { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); - ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*) pack->pBuffer; - strcpy(skp->gmname, gmk->gmname); - strcpy(skp->target, gmk->name); - skp->admin = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - return; -} - -void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMLastName_Struct)) { - std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; - return; - } - GMLastName_Struct* gmln = (GMLastName_Struct*) app->pBuffer; - if (strlen(gmln->lastname) >= 64) { - Message(13, "/LastName: New last name too long. (max=63)"); - } - else { - Client* client = entity_list.GetClientByName(gmln->name); - if (client == 0) { - Message(13, "/LastName: %s not found", gmln->name); - } - else { - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(client->account_name, client->name, "/lastname"); - return; - } - else - - client->ChangeLastName(gmln->lastname); - } - gmln->unknown[0] = 1; - gmln->unknown[1] = 1; - gmln->unknown[2] = 1; - gmln->unknown[3] = 1; - entity_list.QueueClients(this, app, false); - } - return; -} - -void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMToggle_Struct)) { - std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/toggle"); - return; - } - GMToggle_Struct *ts = (GMToggle_Struct *) app->pBuffer; - if (ts->toggle == 0) { - this->Message_StringID(0,TOGGLE_OFF); - //Message(0, "Turning tells OFF"); - tellsoff = true; - } - else if (ts->toggle == 1) { - //Message(0, "Turning tells ON"); - this->Message_StringID(0,TOGGLE_ON); - tellsoff = false; - } - else { - Message(0, "Unkown value in /toggle packet"); - } - UpdateWho(); - return; -} - -void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LFG_Struct)) { - std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; - DumpPacket(app); - return; - } - - // Process incoming packet - LFG_Struct* lfg = (LFG_Struct*) app->pBuffer; - - switch(lfg->value & 0xFF) { - case 0: - if(LFG) { - database.SetLFG(CharacterID(), false); - LFG = false; - LFGComments[0] = '\0'; - } - break; - case 1: - if(!LFG) { - LFG = true; - database.SetLFG(CharacterID(), true); - } - LFGFromLevel = lfg->FromLevel; - LFGToLevel = lfg->ToLevel; - LFGMatchFilter = lfg->MatchFilter; - strcpy(LFGComments, lfg->Comments); - break; - default: - Message(0, "Error: unknown LFG value %i", lfg->value); - } - - UpdateWho(); - - // Issue outgoing packet to notify other clients - EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); - LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; - lfga->spawn_id = this->GetID(); - lfga->lfg = (uint8)LFG; - - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/goto"); - return; - } - GMSummon_Struct* gmg = (GMSummon_Struct*) app->pBuffer; - Mob* gt = entity_list.GetMob(gmg->charname); - if (gt != nullptr) { - this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); - } - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected."); - else { - ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); - memset(pack->pBuffer, 0, pack->size); - ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*) pack->pBuffer; - strcpy(wsgmg->myname, this->GetName()); - strcpy(wsgmg->gotoname, gmg->charname); - wsgmg->admin = admin; - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // This is when a potential purchaser right clicks on this client who is in Trader mode to - // browse their goods. - // - _pkt(TRADING__PACKETS, app); - - TraderClick_Struct* tcs= (TraderClick_Struct*)app->pBuffer; - - if(app->size!=sizeof(TraderClick_Struct)) { - - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); - - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); - - TraderClick_Struct* outtcs=(TraderClick_Struct*)outapp->pBuffer; - - Client* Customer = entity_list.GetClientByID(tcs->TraderID); - - if (Customer) - outtcs->Approval = Customer->WithCustomer(GetID()); - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" - " returned a nullptr pointer"); - return; - } - - outtcs->TraderID = tcs->TraderID; - - outtcs->Unknown008 = 0x3f800000; - - QueuePacket(outapp); - - _pkt(TRADING__PACKETS, outapp); - - if(outtcs->Approval) { - this->BulkSendTraderInventory(Customer->CharacterID()); - Customer->Trader_CustomerBrowsing(this); - } - else - Message_StringID(clientMessageYellow, TRADER_BUSY); - - safe_delete(outapp); - - return; -} - -void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Click_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); - return; - } - - Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer; - - // Send back opcode OP_ShopRequest - tells client to open merchant window. - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - int merchantid=0; - Mob* tmp = entity_list.GetMob(mc->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - int action = 1; - if(merchantid == 0) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = 1; //open... - mco->rate = 1.0; - QueuePacket(outapp); - safe_delete(outapp); - return; - } - if(tmp->IsEngaged()){ - this->Message_StringID(0,MERCHANT_BUSY); - action = 0; - } - if (GetFeigned() || IsInvisible()) - { - Message(0,"You cannot use a merchant right now."); - action = 0; - } - int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); - int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); - if (factionlvl >= 7) { - MerchantRejectMessage(tmp, primaryfaction); - action = 0; - } - - if (tmp->Charmed()) - action = 0; - - // 1199 I don't have time for that now. etc - if (!tmp->CastToNPC()->IsMerchantOpen()) { - tmp->Say_StringID(MakeRandomInt(1199, 1202)); - action = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = action; // Merchant command 0x01 = open - if (RuleB(Merchant, UsePriceMod)){ - mco->rate = 1/((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp,true)); // works - } - else - mco->rate = 1/(RuleR(Merchant, BuyCostMod)); - - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - if (action == 1) - BulkSendMerchantInventory(merchantid,tmp->GetNPCTypeID()); - - return; -} - -void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) -{ - _pkt(TRADING__PACKETS, app); - - if (app->size==sizeof(BazaarSearch_Struct)) { - - BazaarSearch_Struct* bss= (BazaarSearch_Struct*)app->pBuffer; - - this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, - bss->Name, bss->MinPrice*1000, bss->MaxPrice*1000); - } - else if (app->size==sizeof(BazaarWelcome_Struct)) { - - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; - - if (bws->Beginning.Action==BazaarWelcome) - SendBazaarWelcome(); - } - else if (app->size==sizeof(NewBazaarInspect_Struct)) { - - NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(nbis->Name); - if(c) { - ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); - if(inst) - SendItemPacket(0, inst, ItemPacketViewLink); - } - return; - } - else { - _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); - LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); - } - - return; -} - -void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Sell_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", - sizeof(Merchant_Sell_Struct), app->size); - return; - } - RDTSC_Timer t1; - t1.start(); - Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer; -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); - DumpPacket(app); -#endif - - int merchantid; - bool tmpmer_used = false; - Mob* tmp = entity_list.GetMob(mp->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - if (mp->quantity < 1) return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - uint32 item_id = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - if(mp->itemslot == ml.slot){ - item_id = ml.item; - break; - } - } - const Item_Struct* item = nullptr; - uint32 prevcharges = 0; - if (item_id == 0) { //check to see if its on the temporary table - std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; - std::list::const_iterator tmp_itr; - TempMerchantList ml; - for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();++tmp_itr){ - ml = *tmp_itr; - if(mp->itemslot == ml.slot){ - item_id = ml.item; - tmpmer_used = true; - prevcharges = ml.charges; - break; - } - } - } - item = database.GetItem(item_id); - if (!item){ - //error finding item, client didnt get the update packet for whatever reason, roleplay a tad - Message(15,"%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.",tmp->GetCleanName()); - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueCloseClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - return; - } - if (CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) - { - if(prevcharges > item->MaxCharges && item->MaxCharges > 1) - mp->quantity = item->MaxCharges; - else - mp->quantity = prevcharges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); - Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer; - mpo->quantity = mp->quantity; - mpo->playerid = mp->playerid; - mpo->npcid = mp->npcid; - mpo->itemslot=mp->itemslot; - - int16 freeslotid=0; - int16 charges = 0; - if (item->Stackable || item->MaxCharges > 1) - charges = mp->quantity; - else - charges = item->MaxCharges; - - ItemInst* inst = database.CreateItem(item, charges); - - int SinglePrice = 0; - if (RuleB(Merchant, UsePriceMod)) - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); - else - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); - - if(item->MaxCharges > 1) - mpo->price = SinglePrice; - else - mpo->price = SinglePrice * mp->quantity; - if(mpo->price < 0 ) - { - safe_delete(outapp); - safe_delete(inst); - return; - } - - if(!TakeMoneyFromPP(mpo->price)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", - mpo->quantity, item->ID, item->Name, - mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - safe_delete(outapp); - safe_delete(inst); - return; - } - - bool stacked = TryStacking(inst); - if(!stacked) - freeslotid = m_inv.FindFreeSlot(false, true, item->Size); - - //make sure we are not completely full... - if (freeslotid == MainCursor) { - if (m_inv.GetItem(MainCursor) != nullptr) { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - } - - if (freeslotid == INVALID_INDEX) - { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - - std::string packet; - if (!stacked && inst) { - PutItemInInventory(freeslotid, *inst); - SendItemPacket(freeslotid, inst, ItemPacketTrade); - } - else if(!stacked){ - LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); - } - QueuePacket(outapp); - if(inst && tmpmer_used){ - int32 new_charges = prevcharges - mp->quantity; - zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(),item_id,new_charges); - if(new_charges<=0){ - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - } - else { - // Update the charges/quantity in the merchant window - inst->SetCharges(new_charges); - inst->SetPrice(SinglePrice); - inst->SetMerchantSlot(mp->itemslot); - inst->SetMerchantCount(new_charges); - - SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); - } - } - safe_delete(inst); - safe_delete(outapp); - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = 0; - qsaudit->merchant_money.gold = 0; - qsaudit->merchant_money.silver = 0; - qsaudit->merchant_money.copper = 0; - qsaudit->merchant_count = 1; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = (mpo->price / 1000); - qsaudit->char_money.gold = (mpo->price / 100) % 10; - qsaudit->char_money.silver = (mpo->price / 10) % 10; - qsaudit->char_money.copper = mpo->price % 10; - qsaudit->char_count = 0; - - qsaudit->items[0].char_slot = freeslotid; - qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); - qsaudit->items[0].charges = mpo->quantity; - qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - if (RuleB(EventLog, RecordBuyFromMerchant)) - LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); - - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); - } - - t1.stop(); - std::cout << "At 1: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Purchase_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", - sizeof(Merchant_Purchase_Struct), app->size); - return; - } - RDTSC_Timer t1(true); - Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(mp->npcid); - - if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - return; - - uint32 price=0; - uint32 itemid = GetItemIDAt(mp->itemslot); - if(itemid == 0) - return; - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(mp->itemslot); - if(!item || !inst){ - Message(13,"You seemed to have misplaced that item.."); - return; - } - if(mp->quantity > 1) - { - if((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) - return; - } - - if (!item->NoDrop) { - //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); - return; - } - - int cost_quantity = mp->quantity; - if(inst->IsCharged()) - int cost_quantity = 1; - - if (RuleB(Merchant, UsePriceMod)) - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price - else - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))+0.5); - AddMoneyToPP(price,false); - - if (inst->IsStackable() || inst->IsCharged()) - { - unsigned int i_quan = inst->GetCharges(); - if (mp->quantity > i_quan || inst->IsCharged()) - mp->quantity = i_quan; - } - else - mp->quantity = 1; - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, mp->quantity, price, item, false); - - int charges = mp->quantity; - //Hack workaround so usable items with 0 charges aren't simply deleted - if(charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) - charges = 1; - - int freeslot = 0; - if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ - ItemInst* inst2 = inst->Clone(); - if (RuleB(Merchant, UsePriceMod)){ - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false)); - } - else - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); - inst2->SetMerchantSlot(freeslot); - - uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); - - if(inst2->IsStackable()) { - inst2->SetCharges(MerchantQuantity); - } - inst2->SetMerchantCount(MerchantQuantity); - - SendItemPacket(freeslot-1, inst2, ItemPacketMerchant); - safe_delete(inst2); - } - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = (price / 1000); - qsaudit->merchant_money.gold = (price / 100) % 10; - qsaudit->merchant_money.silver = (price / 10) % 10; - qsaudit->merchant_money.copper = price % 10; - qsaudit->merchant_count = 0; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = 0; - qsaudit->char_money.gold = 0; - qsaudit->char_money.silver = 0; - qsaudit->char_money.copper = 0; - qsaudit->char_count = 1; - - qsaudit->items[0].char_slot = mp->itemslot; - qsaudit->items[0].item_id = itemid; - qsaudit->items[0].charges = charges; - qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - // Now remove the item from the player, this happens regardless of outcome - if (!inst->IsStackable()) - this->DeleteItemInInventory(mp->itemslot,0,false); - else - this->DeleteItemInInventory(mp->itemslot,mp->quantity,false); - - //This forces the price to show up correctly for charged items. - if(inst->IsCharged()) - mp->quantity = 1; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); - Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer; - mco->npcid = vendor->GetID(); - mco->itemslot=mp->itemslot; - mco->quantity=mp->quantity; - mco->price=price; - QueuePacket(outapp); - safe_delete(outapp); - SendMoneyUpdate(); - t1.start(); - Save(1); - t1.stop(); - std::cout << "Save took: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) -{ - EQApplicationPacket empty(OP_ShopEndConfirm); - QueuePacket(&empty); - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); - //outapp->pBuffer[0] = 0x0a; - //outapp->pBuffer[1] = 0x66; - //QueuePacket(outapp); - //safe_delete(outapp); - //Save(); - return; -} - -/* -void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CloseContainer_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", - sizeof(CloseContainer_Struct), app->size); - return; - } - - SetTradeskillObject(nullptr); - - ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - object->Close(); - } - return; -} -*/ - -void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) -{ - if (app->size == 0) { - // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. - // Not completely sure if 0 sized is for this or for closing objects as commented out below - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - - return; - - // RoF sends a 0 sized packet for closing objects - /* - Object* object = GetTradeskillObject(); - if (object) { - object->CastToObject()->Close(); - } - */ - } - else - { - if (app->size != sizeof(ClickObjectAction_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", - sizeof(ClickObjectAction_Struct), app->size); - return; - } - - ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - if(oos->open == 0) { - object->Close(); - } else { - LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); - } - } else { - LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); - } - } - - SetTradeskillObject(nullptr); - - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickObject_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", - sizeof(ClickObject_Struct), app->size); - return; - } - - ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(click_object->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - - object->HandleClick(this, click_object); - - std::vector args; - args.push_back(object); - - char buf[10]; - snprintf(buf, 9, "%u", click_object->drop_id); - buf[9] = '\0'; - parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); - } - - // Observed in RoF after OP_ClickObjectAction: - //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - //QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeskillFavorites_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", - sizeof(TradeskillFavorites_Struct), app->size); - return; - } - - TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; - - LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); - - // results show that object_type is combiner type - // some_id = 0 if world combiner, item number otherwise - - // make where clause segment for container(s) - char containers[30]; - if (tsf->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", tsf->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", tsf->object_type, tsf->some_id); - } - - char *query = 0; - char buf[5500]; //gotta be big enough for 500 IDs - - bool first = true; - uint16 r; - char *pos = buf; - - //Assumes item IDs are <10 characters long - for(r = 0; r < 500; r++) { - if(tsf->favorite_recipes[r] == 0) - continue; - - if(first) { - pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); - first = false; - } else { - pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); - } - } - - if(first) //no favorites.... - return; - - //To be a good kid, I should move this SQL somewhere else... - //but im lazy right now, so it stays here - uint32 qlen = 0; - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE tr.enabled <> 0 AND tr.id IN (%s) " - " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 100 ", CharacterID(), buf, containers); - - TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipesSearch_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", - sizeof(RecipesSearch_Struct), app->size); - return; - } - - RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; - rss->query[55] = '\0'; //just to be sure. - - - LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); - - // make where clause segment for container(s) - char containers[30]; - if (rss->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", rss->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", rss->object_type, rss->some_id); - } - - char *query = 0; - char searchclause[140]; //2X rss->query + SQL crap - - //omit the rlike clause if query is empty - if(rss->query[0] != 0) { - char buf[120]; //larger than 2X rss->query - database.DoEscapeString(buf, rss->query, strlen(rss->query)); - - snprintf(searchclause, 139, "name rlike '%s' AND", buf); - } else { - searchclause[0] = '\0'; - } - uint32 qlen = 0; - - //arbitrary limit of 200 recipes, makes sense to me. - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " - " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 200 " - , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); - - TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) -{ - if(app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", - sizeof(uint32), app->size); - return; - } - uint32 *recipe_id = (uint32*) app->pBuffer; - - SendTradeskillDetails(*recipe_id); - - return; -} - -void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipeAutoCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", - sizeof(RecipeAutoCombine_Struct), app->size); - return; - } - - RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; - - Object::HandleAutoCombine(this, rac); - return; -} - -void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(NewCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", - sizeof(NewCombine_Struct), app->size); - return; - } - /*if (m_tradeskill_object == nullptr) { - Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); - return; - }*/ - - //fixed this to work for non-world objects - - // Delegate to tradeskill object to perform combine - NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; - Object::HandleCombine(this, in_combine, m_tradeskill_object); - return; -} - -void Client::Handle_OP_ItemName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemNamePacket_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", - sizeof(ItemNamePacket_Struct), app->size); - return; - } - ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; - const Item_Struct *item = 0; - if ((item = database.GetItem(p->item_id))!=nullptr) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ItemName,sizeof(ItemNamePacket_Struct)); - p=(ItemNamePacket_Struct*)outapp->pBuffer; - memset(p, 0, sizeof(ItemNamePacket_Struct)); - strcpy(p->name,item->Name); - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AugmentItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", - sizeof(AugmentItem_Struct), app->size); - return; - } - - // Delegate to tradeskill object to perform combine - AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; - bool deleteItems = false; - if(GetClientVersion() >= EQClientRoF) - { - ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - - //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - - // Adding augment - if (in_augment->augment_action == 0) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; - //Message(13, "%i AugSlot", aug_slot_id); - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(MainCursor); - - if (tobe_auged && auged_with) - { - if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) - { - tobe_auged->PutAugment(in_augment->augment_index, *auged_with); - - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); - return; - } - - itemOneToPush = tobe_auged->Clone(); - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(MainCursor, 0, true); - if(PutItemInInventory(slot_id, *itemOneToPush, true)) - { - //Message(13, "Sucessfully added an augment to your item!"); - return; - } - else - { - Message(13, "Error: No available slot for end result. Please free up some bag space."); - } - } - else - { - Message(13, "Error in cloning item for augment. Aborted."); - } - - } - else - { - Message(13, "Error: No available slot for augment in that item."); - } - } - } - else if(in_augment->augment_action == 1) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(aug_slot_id); - - ItemInst *old_aug = nullptr; - if(!auged_with) - return; - const uint32 id = auged_with->GetID(); - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - - args.push_back(false); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); - return; - } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); - if(itemOneToPush && itemTwoToPush && auged_with) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); - if(!PutItemInInventory(slot_id, *itemOneToPush, true)) - { - Message(15, "Shouldn't happen, contact an admin!"); - } - - if(PutItemInInventory(MainCursor, *itemTwoToPush, true)) - { - //Message(15, "Successfully removed an augmentation!"); - } - } - } - } - else - { - Object::HandleAugmentation(this, in_augment, m_tradeskill_object); - } - return; -} - -void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickDoor_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); - return; - } - ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; - Doors* currentdoor = entity_list.FindDoor(cd->doorid); - if(!currentdoor) - { - Message(0,"Unable to find door, please notify a GM (DoorID: %i).",cd->doorid); - return; - } - - char buf[20]; - snprintf(buf, 19, "%u", cd->doorid); - buf[19] = '\0'; - std::vector args; - args.push_back(currentdoor); - parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); - - currentdoor->HandleClick(this,0); - return; -} - -void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) -{ - DropItem(MainCursor); - return; -} - -void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(FaceChange_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", - sizeof(FaceChange_Struct), app->size); - return; - } - - // Notify other clients in zone - entity_list.QueueClients(this, app, false); - - FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; - m_pp.haircolor = fc->haircolor; - m_pp.beardcolor = fc->beardcolor; - m_pp.eyecolor1 = fc->eyecolor1; - m_pp.eyecolor2 = fc->eyecolor2; - m_pp.hairstyle = fc->hairstyle; - m_pp.face = fc->face; - m_pp.beard = fc->beard; - m_pp.drakkin_heritage = fc->drakkin_heritage; - m_pp.drakkin_tattoo = fc->drakkin_tattoo; - m_pp.drakkin_details = fc->drakkin_details; - Save(); - Message_StringID(13,FACE_ACCEPTED); - //Message(13, "Facial features updated."); - return; -} - -void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) -{ - //this seems to be the initial invite to form a group - Handle_OP_GroupInvite2(app); -} - -void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupInvite_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", - sizeof(GroupInvite_Struct), app->size); - return; - } - - GroupInvite_Struct* gis = (GroupInvite_Struct*) app->pBuffer; - - Mob *Invitee = entity_list.GetMob(gis->invitee_name); - - if(Invitee == this) - { - Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); - return; - } - - if(Invitee) { - if(Invitee->IsClient()) { - if((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || - (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) - { - if(app->GetOpcode() == OP_GroupInvite2) - { - //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we - //Don't have to deal with GroupFollow2 crap. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(outapp->pBuffer, app->pBuffer, outapp->size); - Invitee->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - return; - } - else - { - //The correct opcode, no reason to bother wasting time reconstructing the packet - Invitee->CastToClient()->QueuePacket(app); - } - } - } -#ifdef BOTS - else if(Invitee->IsBot()) { - Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); - } -#endif - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - void Client::Handle_OP_GroupAcknowledge(const EQApplicationPacket *app) { return; @@ -7030,12 +6343,12 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } - GroupCancel_Struct* gf = (GroupCancel_Struct*) app->pBuffer; + GroupCancel_Struct* gf = (GroupCancel_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr) + if (inviter != nullptr) { - if(inviter->IsClient()) + if (inviter->IsClient()) inviter->CastToClient()->QueuePacket(app); } else @@ -7050,6 +6363,193 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } +void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) +{ + //should check for leader, only they should be able to do this.. + Group* group = GetGroup(); + if (group) + group->DisbandGroup(); + + if (LFP) + UpdateLFP(); + + return; +} + +void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupGeneric_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", + sizeof(GroupGeneric_Struct), app->size); + return; + } + + LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + + GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; + + Raid *raid = entity_list.GetRaidByClient(this); + if (raid){ + Mob* memberToDisband = nullptr; + + if (!raid->IsGroupLeader(GetName())) + memberToDisband = this; + else + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (!memberToDisband) + memberToDisband = this; + + if (!memberToDisband->IsClient()) + return; + + //we have a raid.. see if we're in a raid group + uint32 grp = raid->GetGroup(memberToDisband->GetName()); + bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; + if (grp < 12){ + if (wasGrpLdr){ + raid->SetGroupLeader(memberToDisband->GetName(), false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (raid->members[x].GroupNumber == grp) + { + if (strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) + { + raid->SetGroupLeader(raid->members[x].membername); + break; + } + } + } + } + raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); + raid->GroupUpdate(grp); //break + //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); + //raid->SendGroupUpdate(memberToDisband->CastToClient()); + raid->SendGroupDisband(memberToDisband->CastToClient()); + } + //we're done + return; + } + + Group* group = GetGroup(); + + if (!group) + return; + +#ifdef BOTS + // this block is necessary to allow more control over controlling how bots are zoned or camped. + if (Bot::GroupHasBot(group)) { + if (group->IsLeader(this)) { + if ((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { + Bot::ProcessBotGroupDisband(this, std::string()); + } + else { + Mob* tempMember = entity_list.GetMob(gd->name2); + if (tempMember) { + if (tempMember->IsBot()) + Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); + } + } + } + } +#endif + if ((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { + group->DisbandGroup(); + if (GetMerc() != nullptr) + GetMerc()->Suspend(); + } + else { + Mob* memberToDisband = nullptr; + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (memberToDisband) { + if (group->IsLeader(this)) { + // the group leader can kick other members out of the group... + //group->DelMember(memberToDisband,false); + if (memberToDisband->IsClient()) + { + group->DelMember(memberToDisband, false); + Client* memberClient = memberToDisband->CastToClient(); + Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); + if (memberClient && memberMerc && group) + { + if (!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { + Group *g = new Group(memberClient); + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + if (Merc::AddMercToGroup(memberMerc, g)) { + database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); + database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); + database.RefreshGroupFromDB(memberClient); + } + } + } + } + else if (memberToDisband->IsMerc()) { + memberToDisband->CastToMerc()->Suspend(); + } + } + else { + // ...but other members can only remove themselves + group->DelMember(this, false); + + if (!IsGrouped() && GetMerc() != nullptr) { + if (!IsGrouped()) { + Group *g = new Group(this); + + if (!g) { + delete g; + g = nullptr; + return; + } + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + + if (Merc::AddMercToGroup(GetMerc(), g)) { + database.SetGroupLeaderName(g->GetID(), this->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); + database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); + database.RefreshGroupFromDB(this); + } + else + { + if (GetMerc()) + GetMerc()->Depop(); + } + } + } + } + } + else + LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); + } + if (LFP) { + // If we are looking for players, update to show we are on our own now. + UpdateLFP(); + } + + return; +} + void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) { Handle_OP_GroupFollow2(app); @@ -7063,42 +6563,42 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) return; } - if(LFP) { + if (LFP) { // If we were looking for players to start our own group, but we accept an invitation to another // group, turn LFP off. database.SetLFP(CharacterID(), false); worldserver.StopLFP(CharacterID()); } - GroupGeneric_Struct* gf = (GroupGeneric_Struct*) app->pBuffer; + GroupGeneric_Struct* gf = (GroupGeneric_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr && inviter->IsClient()) { + if (inviter != nullptr && inviter->IsClient()) { isgrouped = true; - strn0cpy(gf->name1,inviter->GetName(), 64); - strn0cpy(gf->name2,this->GetName(), 64); + strn0cpy(gf->name1, inviter->GetName(), 64); + strn0cpy(gf->name2, this->GetName(), 64); Raid* raid = entity_list.GetRaidByClient(inviter->CastToClient()); Raid* iraid = entity_list.GetRaidByClient(this); //inviter has a raid don't do group stuff instead do raid stuff! - if(raid){ + if (raid){ // Suspend the merc while in a raid (maybe a rule could be added for this) if (GetMerc()) GetMerc()->Suspend(); uint32 groupToUse = 0xFFFFFFFF; - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(raid->members[x].member){ //this assumes the inviter is in the zone - if(raid->members[x].member == inviter->CastToClient()){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (raid->members[x].member){ //this assumes the inviter is in the zone + if (raid->members[x].member == inviter->CastToClient()){ groupToUse = raid->members[x].GroupNumber; break; } } } - if(iraid == raid){ //both in same raid + if (iraid == raid){ //both in same raid uint32 ngid = raid->GetGroup(inviter->GetName()); - if(raid->GroupCount(ngid) < 6){ + if (raid->GroupCount(ngid) < 6){ raid->MoveMember(GetName(), ngid); raid->SendGroupDisband(this); //raid->SendRaidGroupAdd(GetName(), ngid); @@ -7107,8 +6607,8 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } return; } - if(raid->RaidCount() < MAX_RAID_MEMBERS){ - if(raid->GroupCount(groupToUse) < 6){ + if (raid->RaidCount() < MAX_RAID_MEMBERS){ + if (raid->GroupCount(groupToUse) < 6){ raid->SendRaidCreate(this); raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this, groupToUse); @@ -7116,7 +6616,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) //raid->SendRaidGroupAdd(GetName(), groupToUse); //raid->SendGroupUpdate(this); raid->GroupUpdate(groupToUse); //break - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -7126,7 +6626,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this); raid->SendBulkRaid(this); - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -7136,14 +6636,14 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) Group* group = entity_list.GetGroupByClient(inviter->CastToClient()); - if(!group){ + if (!group){ //Make new group group = new Group(inviter); - if(!group) + if (!group) return; entity_list.AddGroup(group); - if(group->GetID() == 0) { + if (group->GetID() == 0) { Message(13, "Unable to get new group id. Cannot create group."); inviter->Message(13, "Unable to get new group id. Cannot create group."); return; @@ -7156,10 +6656,10 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) group->UpdateGroupAAs(); //Invite the inviter into the group first.....dont ask - if(inviter->CastToClient()->GetClientVersion() < EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() < EQClientSoD) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* outgj=(GroupJoin_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* outgj = (GroupJoin_Struct*)outapp->pBuffer; strcpy(outgj->membername, inviter->GetName()); strcpy(outgj->yourname, inviter->GetName()); outgj->action = groupActInviteInitial; // 'You have formed the group'. @@ -7179,28 +6679,28 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } - if(!group) + if (!group) return; inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted - if(!group->AddMember(this)) + if (!group->AddMember(this)) return; - if(inviter->CastToClient()->IsLFP()) { + if (inviter->CastToClient()->IsLFP()) { // If the player who invited us to a group is LFP, have them update world now that we have joined // their group. inviter->CastToClient()->UpdateLFP(); } - if(GetClientVersion() >= EQClientSoD) + if (GetClientVersion() >= EQClientSoD) SendGroupJoinAcknowledge(); database.RefreshGroupFromDB(this); group->SendHPPacketsTo(this); // Temporary hack for SoD, as things seem to work quite differently - if(inviter->CastToClient()->GetClientVersion() >= EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() >= EQClientSoD) database.RefreshGroupFromDB(inviter->CastToClient()); // Add the merc back into the new group @@ -7221,7 +6721,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) worldserver.SendPacket(pack); safe_delete(pack); } - else if(inviter == nullptr) + else if (inviter == nullptr) { ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; @@ -7233,4233 +6733,137 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } -void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) { - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); + //this seems to be the initial invite to form a group + Handle_OP_GroupInvite2(app); +} + +void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupInvite_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", + sizeof(GroupInvite_Struct), app->size); return; } - LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer; - GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer; + Mob *Invitee = entity_list.GetMob(gis->invitee_name); - Raid *raid = entity_list.GetRaidByClient(this); - if(raid){ - Mob* memberToDisband = nullptr; + if (Invitee == this) + { + Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); + return; + } - if(!raid->IsGroupLeader(GetName())) - memberToDisband = this; - else - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(!memberToDisband) - memberToDisband = this; - - if(!memberToDisband->IsClient()) - return; - - //we have a raid.. see if we're in a raid group - uint32 grp = raid->GetGroup(memberToDisband->GetName()); - bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; - if(grp < 12){ - if(wasGrpLdr){ - raid->SetGroupLeader(memberToDisband->GetName(), false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) + if (Invitee) { + if (Invitee->IsClient()) { + if ((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || + (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) + { + if (app->GetOpcode() == OP_GroupInvite2) { - if(raid->members[x].GroupNumber == grp) - { - if(strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) - { - raid->SetGroupLeader(raid->members[x].membername); - break; - } - } + //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we + //Don't have to deal with GroupFollow2 crap. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(outapp->pBuffer, app->pBuffer, outapp->size); + Invitee->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + return; } - } - raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); - raid->GroupUpdate(grp); //break - //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); - //raid->SendGroupUpdate(memberToDisband->CastToClient()); - raid->SendGroupDisband(memberToDisband->CastToClient()); - } - //we're done - return; - } - - Group* group = GetGroup(); - - if(!group) - return; - -#ifdef BOTS - // this block is necessary to allow more control over controlling how bots are zoned or camped. - if(Bot::GroupHasBot(group)) { - if(group->IsLeader(this)) { - if((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { - Bot::ProcessBotGroupDisband(this, std::string()); - } else { - Mob* tempMember = entity_list.GetMob(gd->name2); - if(tempMember) { - if(tempMember->IsBot()) - Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); - } - } - } - } -#endif - if((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { - group->DisbandGroup(); - if(GetMerc() != nullptr) - GetMerc()->Suspend(); - } else { - Mob* memberToDisband = nullptr; - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(memberToDisband ) { - if(group->IsLeader(this)) { - // the group leader can kick other members out of the group... - //group->DelMember(memberToDisband,false); - if(memberToDisband->IsClient()) - { - group->DelMember(memberToDisband,false); - Client* memberClient = memberToDisband->CastToClient(); - Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); - if(memberClient && memberMerc && group) - { - if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { - Group *g = new Group(memberClient); - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - if(Merc::AddMercToGroup(memberMerc, g)) { - database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); - database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); - database.RefreshGroupFromDB(memberClient); - } - } - } - } - else if(memberToDisband->IsMerc()) { - memberToDisband->CastToMerc()->Suspend(); - } - } - else { - // ...but other members can only remove themselves - group->DelMember(this,false); - - if(!IsGrouped() && GetMerc() != nullptr) { - if(!IsGrouped()) { - Group *g = new Group(this); - - if(!g) { - delete g; - g = nullptr; - return; - } - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - - if(Merc::AddMercToGroup(GetMerc(), g)) { - database.SetGroupLeaderName(g->GetID(), this->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); - database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); - database.RefreshGroupFromDB(this); - } - else - { - if(GetMerc()) - GetMerc()->Depop(); - } - } - } - } - } - else - LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); - } - if(LFP) { - // If we are looking for players, update to show we are on our own now. - UpdateLFP(); - } - - return; -} - -void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) -{ -//should check for leader, only they should be able to do this.. - Group* group = GetGroup(); - if (group) - group->DisbandGroup(); - - if(LFP) - UpdateLFP(); - - return; -} - -void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/emote"); - return; - } - if (app->size != sizeof(GMEmoteZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); - return; - } - GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; - char* newmessage=0; - if(strstr(gmez->text,"^")==0) - entity_list.Message(0, 15, gmez->text); - else{ - for(newmessage = strtok((char*)gmez->text,"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) - entity_list.Message(0, 15, newmessage); - } - return; -} - -void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(Inspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); - return; - } - - Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer; - Mob* tmp = entity_list.GetMob(ins->TargetID); - - if(tmp != 0 && tmp->IsClient()) { - if(tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target - // Inspecting an SoF or later client will make the server handle the request - else { ProcessInspectRequest(tmp->CastToClient(), this); } - } - -#ifdef BOTS - if(tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } -#endif - - return; -} - -void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectResponse_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); - return; - } - - //Fills the app sent from client. - EQApplicationPacket* outapp = app->Copy(); - InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; - Mob* tmp = entity_list.GetMob(insr->TargetID); - const Item_Struct* item = nullptr; - - for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { - const ItemInst* inst = GetInv().GetItem(L); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; - } - else { insr->itemicons[L] = 0xFFFFFFFF; } - } - - const ItemInst* inst = GetInv().GetItem(MainAmmo); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - // another one..I did these, didn't I!!? - strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); - insr->itemicons[SoF::slots::MainAmmo] = item->Icon; - } - else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) insr->text; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); - - if(tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester - - return; -} - -void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectMessage_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); - return; - } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) app->pBuffer; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); -} - -#if 0 // I dont think there's an op for this now, and we check this - // when the client is sitting -void Client::Handle_OP_Medding(const EQApplicationPacket *app) -{ - if (app->pBuffer[0]) - medding = true; - else - medding = false; - return; -} -#endif - -void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(DeleteSpell_Struct)) - return; - - EQApplicationPacket* outapp = app->Copy(); - DeleteSpell_Struct* dss = (DeleteSpell_Struct*) outapp->pBuffer; - - if(dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) - return; - - if(m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { - m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; - dss->success = 1; - } - else - dss->success = 0; - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(LoadSpellSet_Struct)) { - printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n",sizeof(LoadSpellSet_Struct),app->size); - return; - } - int i; - LoadSpellSet_Struct* ss=(LoadSpellSet_Struct*)app->pBuffer; - for(i=0;ispell[i] != 0xFFFFFFFF) - UnmemSpell(i,true); - } -} - - -void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n",sizeof(PetitionBug_Struct),app->size); - else{ - Message(0, "Petition Bugs are not supported, please use /bug."); - } - return; -} - -void Client::Handle_OP_Bug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else{ - BugStruct* bug=(BugStruct*)app->pBuffer; - database.UpdateBug(bug); - } - return; -} - -void Client::Handle_OP_Petition(const EQApplicationPacket *app) -{ - if (app->size <= 1) - return; - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) - { - Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); - return; - }*/ - else - { - if(petition_list.FindPetitionByAccountName(AccountName())) - { - Message(0,"You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); - return; - } - Petition* pet = new Petition(CharacterID()); - pet->SetAName(this->AccountName()); - pet->SetClass(this->GetClass()); - pet->SetLevel(this->GetLevel()); - pet->SetCName(this->GetName()); - pet->SetRace(this->GetRace()); - pet->SetLastGM(""); - pet->SetCName(this->GetName()); - pet->SetPetitionText((char*) app->pBuffer); - pet->SetZone(zone->GetZoneID()); - pet->SetUrgency(0); - petition_list.AddPetition(pet); - database.InsertPetitionToDB(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); - } - return; -} - -void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Petition_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); - return; - } - Petition_Struct* inpet = (Petition_Struct*) app->pBuffer; - - Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); - //if (inpet->urgency != pet->GetUrgency()) - pet->SetUrgency(inpet->urgency); - pet->SetLastGM(this->GetName()); - pet->SetGMText(inpet->gmtext); - - pet->SetCheckedOut(false); - petition_list.UpdatePetition(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) -{ - Handle_OP_PetitionDelete(app); -} - -void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetitionUpdate_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); - PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer; - pet->petnumber = *((int*) app->pBuffer); - pet->color = 0x00; - pet->status = 0xFFFFFFFF; - pet->senttime = 0; - strcpy(pet->accountid, ""); - strcpy(pet->gmsenttoo, ""); - pet->quetotal = petition_list.GetTotalPetitions(); - strcpy(pet->charname, ""); - FastQueuePacket(&outapp); - - if (petition_list.DeletePetition(pet->petnumber) == -1) - std::cout << "Something is borked with: " << pet->petnumber << std::endl; - petition_list.ClearPetitions(); - petition_list.UpdateGMQueue(); - petition_list.ReadDatabase(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetCommand_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); - return; - } - char val1[20]={0}; - PetCommand_Struct* pet = (PetCommand_Struct*) app->pBuffer; - Mob* mypet = this->GetPet(); - - if(!mypet || pet->command == PET_LEADER) - { - if(pet->command == PET_LEADER) - { - if(mypet && (!GetTarget() || GetTarget() == mypet)) - { - mypet->Say_StringID(PET_LEADERIS, GetName()); - } - else if((mypet = GetTarget())) - { - Mob *Owner = mypet->GetOwner(); - if(Owner) - mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); else - mypet->Say_StringID(I_FOLLOW_NOONE); - } - } - - return; - } - - if(mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) - return; - - // just let the command "/pet get lost" work for familiars - if(mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) - return; - - uint32 PetCommand = pet->command; - - // Handle Sit/Stand toggle in UF and later. - if(GetClientVersion() >= EQClientUnderfoot) - { - if(PetCommand == PET_SITDOWN) - if(mypet->GetPetOrder() == SPO_Sit) - PetCommand = PET_STANDUP; - } - - switch(PetCommand) - { - case PET_ATTACK: { - if (!GetTarget()) - break; - if (GetTarget()->IsMezzed()) { - Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); - break; - } - if (mypet->IsFeared()) - break; //prevent pet from attacking stuff while feared - - if (!mypet->IsAttackAllowed(GetTarget())) { - mypet->Say_StringID(NOT_LEGAL_TARGET); - break; - } - - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { - if (mypet->IsHeld()) { - if (!mypet->IsFocused()) { - mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. - if(mypet->GetPetOrder() != SPO_Guard) - mypet->SetPetOrder(SPO_Follow); - } else { - mypet->SetTarget(GetTarget()); - } + { + //The correct opcode, no reason to bother wasting time reconstructing the packet + Invitee->CastToClient()->QueuePacket(app); } - zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); - Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); } } - break; +#ifdef BOTS + else if (Invitee->IsBot()) { + Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); + } +#endif } - case PET_BACKOFF: { - if (mypet->IsFeared()) break; //keeps pet running while feared + else + { + ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_CALMING); - mypet->WipeHateList(); - mypet->SetTarget(nullptr); - } - break; - } - case PET_HEALTHREPORT: { - Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); - mypet->ShowBuffList(this); - //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); - break; - } - case PET_GETLOST: { - if (mypet->Charmed()) - break; - if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { - // eqlive ignores this command - // we could just remove the charm - // and continue - mypet->BuffFadeByEffect(SE_Charm); - break; - } else { - SetPet(nullptr); - } +void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); - mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); + GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; - //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. - /* - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); - } - */ + Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); - break; - } - case PET_GUARDHERE: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group* g = GetGroup(); - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - if(mypet->IsNPC()) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); - mypet->SetPetOrder(SPO_Guard); - mypet->CastToNPC()->SaveGuardSpot(); - } + if (NewLeader && g) + { + if (g->IsLeader(this)) + g->ChangeLeader(NewLeader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - case PET_FOLLOWME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; +void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupRole_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); + DumpPacket(app); + return; } - case PET_TAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_DO_TAUNT); - mypet->CastToNPC()->SetTaunting(true); - } - break; - } - case PET_NOTAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_NO_TAUNT); - mypet->CastToNPC()->SetTaunting(false); - } - break; - } - case PET_GUARDME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_SITDOWN: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group *g = GetGroup(); - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); - } - break; - } - case PET_STANDUP: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + if (!g) + return; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } + switch (grs->RoleNumber) + { + case 1: //Main Tank + { + if (grs->Toggle) + g->DelegateMainTank(grs->Name1, grs->Toggle); + else + g->UnDelegateMainTank(grs->Name1, grs->Toggle); break; } - case PET_SLUMBER: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if(mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); - } + case 2: //Main Assist + { + if (grs->Toggle) + g->DelegateMainAssist(grs->Name1, grs->Toggle); + else + g->UnDelegateMainAssist(grs->Name1, grs->Toggle); break; } - case PET_HOLD: { - if(GetAA(aaPetDiscipline) && mypet->IsNPC()){ - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_ON: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_OFF: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) - mypet->SetHeld(false); - break; - } - case PET_NOCAST: { - if(GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsNoCast()) { - Message_StringID(MT_PetResponse, PET_CASTING); - mypet->CastToNPC()->SetNoCast(false); - } else { - Message_StringID(MT_PetResponse, PET_NOT_CASTING); - mypet->CastToNPC()->SetNoCast(true); - } - } - break; - } - case PET_FOCUS: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } else { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_ON: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (!mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_OFF: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } - } + case 3: //Puller + { + if (grs->Toggle) + g->DelegatePuller(grs->Name1, grs->Toggle); + else + g->UnDelegatePuller(grs->Name1, grs->Toggle); break; } default: - printf("Client attempted to use a unknown pet command:\n"); break; } } -void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->SetCheckedOut(false); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) -{ -#ifdef _EQDEBUG - printf("%s looking at petitions..\n",this->GetName()); -#endif - return; -} - -void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) -{ - if (app->size < 2) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); - return; - } - if(petition_list.DeletePetitionByCharName((char*)app->pBuffer)) - Message_StringID(0,PETITION_DELETED); - else - Message_StringID(0,PETITION_NO_DELETE); - return; -} - -void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->AddCheckout(); - getpet->SetCheckedOut(true); - getpet->SendPetitionToPlayer(this->CastToClient()); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) -{ - // This is When Client Asks for Petition Again and Again... - // break is here because it floods the zones and causes lag if it - // Were to actually do something:P We update on our own schedule now. - return; -} - -void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BookRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); - return; - } - BookRequest_Struct* book = (BookRequest_Struct*) app->pBuffer; - ReadBook(book); - if(GetClientVersion() >= EQClientSoF) - { - EQApplicationPacket EndOfBook(OP_FinishWindow, 0); - QueuePacket(&EndOfBook); - } - return; -} - -void Client::Handle_OP_Emote(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Emote_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Emote: got %d, expected %d", app->size, - sizeof(Emote_Struct)); - DumpPacket(app); - return; - } - - // Calculate new packet dimensions - Emote_Struct* in = (Emote_Struct*)app->pBuffer; - in->message[1023] = '\0'; - - const char* name = GetName(); - uint32 len_name = strlen(name); - uint32 len_msg = strlen(in->message); - // crash protection -- cheater - if (len_msg > 512) { - in->message[512] = '\0'; - len_msg = 512; - } - uint32 len_packet = sizeof(in->unknown01) + len_name - + len_msg + 1; - - // Construct outgoing packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); - Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; - out->unknown01 = in->unknown01; - memcpy(out->message, name, len_name); - memcpy(&out->message[len_name], in->message, len_msg); - - /* - if (target && target->IsClient()) { - entity_list.QueueCloseClients(this, outapp, false, 100, target); - - cptr = outapp->pBuffer + 2; - - // not sure if live does this or not. thought it was a nice feature, but would take a lot to - // clean up grammatical and other errors. Maybe with a regex parser... - replacestr((char *)cptr, target->GetName(), "you"); - replacestr((char *)cptr, " he", " you"); - replacestr((char *)cptr, " she", " you"); - replacestr((char *)cptr, " him", " you"); - replacestr((char *)cptr, " her", " you"); - target->CastToClient()->QueuePacket(outapp); - - } - else - */ - entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); - - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Animation(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Animation_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Animation: got %d, expected %d", app->size, - sizeof(Animation_Struct)); - DumpPacket(app); - return; - } - - Animation_Struct *s = (Animation_Struct *) app->pBuffer; - - //might verify spawn ID, but it wouldent affect anything - - DoAnim(s->action, s->value); - - return; -} - -void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_SetServerFilter: got %d, expected %d", app->size, - sizeof(SetServerFilter_Struct)); - DumpPacket(app); - return; - } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; -} - -void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMDelCorpse_Struct)) - return; - if(this->Admin() < commandEditPlayerCorpses) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); - return; - } - GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; - Mob* corpse = entity_list.GetMob(dc->corpsename); - if(corpse==0) { - return; - } - if(corpse->IsCorpse() != true) { - return; - } - corpse->CastToCorpse()->Delete(); - std::cout << name << " deleted corpse " << dc->corpsename << std::endl; - Message(13, "Corpse %s deleted.", dc->corpsename); - return; -} - -void Client::Handle_OP_GMKick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMKick_Struct)) - return; - if(this->Admin() < minStatusToKick) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kick"); - return; - } - GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; - - Client* client = entity_list.GetClientByName(gmk->name); - if(client==0) { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, gmk->gmname); - strcpy(skp->name, gmk->name); - skp->adminrank = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - else { - entity_list.QueueClients(this,app); - //client->Kick(); - } - return; -} - -void Client::Handle_OP_GMServers(const EQApplicationPacket *app) -{ - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName())+2); - memset(pack->pBuffer, (uint8) admin, 1); - strcpy((char *) &pack->pBuffer[1], this->GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_Illusion(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Illusion_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, - sizeof(Illusion_Struct)); - DumpPacket(app); - return; - } - - if(!GetGM()) - { - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); - return; - } - - Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; - //these need to be implemented - /* - texture = bnpc->texture; - helmtexture = bnpc->helmtexture; - luclinface = bnpc->luclinface; - */ - race = bnpc->race; - size = 0; - - entity_list.QueueClients(this,app); - return; -} - -void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); - return; - } - if (app->size != sizeof(BecomeNPC_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); - return; - } - //entity_list.QueueClients(this, app, false); - BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; - - Mob* cli = (Mob*) entity_list.GetMob(bnpc->id); - if(cli==0) - return; - - if(cli->IsClient()) - cli->CastToClient()->QueuePacket(app); - cli->SendAppearancePacket(AT_NPCName, 1, true); - cli->CastToClient()->SetBecomeNPC(true); - cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); - cli->Message_StringID(0,TOGGLE_OFF); - cli->CastToClient()->tellsoff = true; - //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. - return; -} - -void Client::Handle_OP_Fishing(const EQApplicationPacket *app) -{ - if(!p_timers.Expired(&database, pTimerFishing, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - - if (CanFish()) { - parse->EventPlayer(EVENT_FISH_START, this, "", 0); - - //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) - p_timers.Start(pTimerFishing, FishingReuseTime-1); - fishing_timer.Start(); - } - return; -// Changes made based on Bobs work on foraging. Now can set items in the forage database table to -// forage for. -} - -void Client::Handle_OP_Forage(const EQApplicationPacket *app) -{ - - if(!p_timers.Expired(&database, pTimerForaging, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerForaging, ForagingReuseTime-1); - - ForageItem(); - - return; -} - -void Client::Handle_OP_Mend(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillMend)) - return; - - if(!p_timers.Expired(&database, pTimerMend, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerMend, MendReuseTime-1); - - int mendhp = GetMaxHP() / 4; - int currenthp = GetHP(); - if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { - - int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; - - if(MakeRandomInt(0,99) < criticalchance){ - mendhp *= 2; - Message_StringID(4,MEND_CRITICAL); - } - SetHP(GetHP() + mendhp); - SendHPUpdate(); - Message_StringID(4,MEND_SUCCESS); - } else { - /* the purpose of the following is to make the chance to worsen wounds much less common, - which is more consistent with the way eq live works. - according to my math, this should result in the following probability: - 0 skill - 25% chance to worsen - 20 skill - 23% chance to worsen - 50 skill - 16% chance to worsen */ - if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend),100) < 75) && (MakeRandomInt(1, 3) == 1)) - { - SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); - SendHPUpdate(); - Message_StringID(4,MEND_WORSEN); - } - else - Message_StringID(4,MEND_FAIL); - } - - CheckIncreaseSkill(SkillMend, nullptr, 10); - return; -} - -void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) -{ - if(!ClientFinishedLoading()) - { - SetHP(GetHP()-1); - return; - } - - if(app->size != sizeof(EnvDamage2_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, - sizeof(EnvDamage2_Struct)); - DumpPacket(app); - return; - } - EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; - if(admin >= minStatusToAvoidFalling && GetGM()){ - Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } else if(GetInvul()) { - Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } - - int damage = ed->damage; - - if (ed->dmgtype == 252) { - - switch(GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then - case 1: - damage = damage * 95 / 100; - break; - case 2: - damage = damage * 90 / 100; - break; - case 3: - damage = damage * 80 / 100; - break; - } - } - - if(damage < 0) - damage = 31337; - - else if(zone->GetZoneID() == 183 || zone->GetZoneID() == 184) - return; - else - SetHP(GetHP() - damage); - - if(GetHP() <= 0) - { - mod_client_death_env(); - - Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); - } - SendHPUpdate(); - return; -} - -void Client::Handle_OP_Damage(const EQApplicationPacket *app) -{ - if(app->size != sizeof(CombatDamage_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, - sizeof(CombatDamage_Struct)); - DumpPacket(app); - return; - } - - // Broadcast to other clients - CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; - //dont send to originator of falling damage packets - entity_list.QueueClients(this, app, (damage->type==DamageTypeFalling)); - return; -} - -void Client::Handle_OP_AAAction(const EQApplicationPacket *app) -{ - mlog(AA__IN, "Received OP_AAAction"); - mpkt(AA__IN, app); - - if(app->size!=sizeof(AA_Action)){ - printf("Error! OP_AAAction size didnt match!\n"); - return; - } - AA_Action* action=(AA_Action*)app->pBuffer; - - if(action->action == aaActionActivate) {//AA Hotkey - mlog(AA__MESSAGE, "Activating AA %d", action->ability); - ActivateAA((aaID) action->ability); - } else if(action->action == aaActionBuy) { - BuyAA(action); - } - else if(action->action == aaActionDisableEXP){ //Turn Off AA Exp - if(m_epp.perAA > 0) - Message_StringID(0, AA_OFF); - m_epp.perAA = 0; - SendAAStats(); - } else if(action->action == aaActionSetEXP) { - if(m_epp.perAA == 0) - Message_StringID(0, AA_ON); - m_epp.perAA = action->exp_value; - if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check - // send an update - SendAAStats(); - SendAATable(); - } else { - printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); - } - - return; -} - -void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // Client has elected to buy an item from a Trader - // - _pkt(TRADING__PACKETS, app); - - if(app->size==sizeof(TraderBuy_Struct)){ - - TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; - - if(Client* Trader=entity_list.GetClientByID(tbs->TraderID)){ - - BuyTraderItem(tbs,Trader,app); - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - } - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); - - } - return; -} - -void Client::Handle_OP_Trader(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. - // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. - - _pkt(TRADING__PACKETS, app); - - uint32 max_items = 80; - - /* - if (GetClientVersion() >= EQClientRoF) - max_items = 200; - */ - - //Show Items - if(app->size==sizeof(Trader_ShowItems_Struct)) - { - Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; - - switch(sis->Code) - { - case BazaarTrader_EndTraderMode: { - Trader_EndTrader(); - break; - } - case BazaarTrader_EndTransaction: { - - Client* c=entity_list.GetClientByID(sis->TraderID); - if(c) - c->WithCustomer(0); - else - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - - break; - } - case BazaarTrader_ShowItems: { - Trader_ShowItems(); - break; - } - default: { - _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); - break; - } - } - } - else if(app->size==sizeof(ClickTrader_Struct)) - { - if(Buyer) { - Trader_EndTrader(); - Message(13, "You cannot be a Trader and Buyer at the same time."); - return; - } - - ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; - - if(ints->Code==BazaarTrader_StartTraderMode) - { - GetItems_Struct* gis=GetTraderItems(); - - // Verify there are no NODROP or items with a zero price - bool TradeItemsValid = true; - - for(uint32 i = 0; i < max_items; i++) { - - if(gis->Items[i] == 0) break; - - if(ints->ItemCost[i] == 0) { - Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - const Item_Struct *Item = database.GetItem(gis->Items[i]); - - if(!Item) { - Message(13, "Unexpected error. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - - if(Item->NoDrop == 0) { - Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - } - - if(!TradeItemsValid) { - Trader_EndTrader(); - return; - } - - for (uint32 i = 0; i < max_items; i++) { - if(database.GetItem(gis->Items[i])) { - database.SaveTraderItem(this->CharacterID(),gis->Items[i],gis->SerialNumber[i], - gis->Charges[i],ints->ItemCost[i],i); - } else { - //return; //sony doesnt memset so assume done on first bad item - break; - } - - } - safe_delete(gis); - - this->Trader_StartTrader(); - - if (GetClientVersion() >= EQClientRoF) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); - TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; - tss->Code = BazaarTrader_StartTraderMode2; - QueuePacket(outapp); - _pkt(TRADING__PACKETS, outapp); - safe_delete(outapp); - } - } - else if (app->size==sizeof(TraderStatus_Struct)) - { - TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; - - if(tss->Code==BazaarTrader_ShowItems) - { - Trader_ShowItems(); - } - } - else { - _log(TRADING__CLIENT,"Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", - ints->Code); - - LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); - } - } - - else if(app->size==sizeof(TraderPriceUpdate_Struct)) - { - HandleTraderPriceUpdate(app); - } - else { - _log(TRADING__CLIENT,"Unknown size for OP_Trader: %i\n", app->size); - LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); - DumpPacket(app); - return; - } - - return; -} - -void Client::Handle_OP_GMFind(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/find"); - return; - } - if (app->size != sizeof(GMSummon_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); - return; - } - //Break down incoming - GMSummon_Struct* request=(GMSummon_Struct*)app->pBuffer; - //Create a new outgoing - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); - GMSummon_Struct* foundplayer=(GMSummon_Struct*)outapp->pBuffer; - //Copy the constants - strcpy(foundplayer->charname,request->charname); - strcpy(foundplayer->gmname, request->gmname); - //Check if the NPC exits intrazone... - Mob* gt = entity_list.GetMob(request->charname); - if (gt != 0) { - foundplayer->success=1; - foundplayer->x=(int32)gt->GetX(); - foundplayer->y=(int32)gt->GetY(); - - foundplayer->z=(int32)gt->GetZ(); - foundplayer->zoneID=zone->GetZoneID(); - } - //Send the packet... - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PickPocket_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); - DumpPacket(app); - } - - if(!HasSkill(SkillPickPockets)) - { - return; - } - - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13,"Ability recovery time not yet met."); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); - return; - } - PickPocket_Struct* pick_in = (PickPocket_Struct*) app->pBuffer; - - Mob* victim = entity_list.GetMob(pick_in->to); - if (!victim) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - if (victim == this){ - Message(0,"You catch yourself red-handed."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->GetOwnerID()){ - Message(0,"You cannot steal from pets!"); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->IsNPC()){ - victim->CastToNPC()->PickPocket(this); - } - else{ - Message(0,"Stealing from clients not yet supported."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BindWound_Struct)){ - LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); - DumpPacket(app); - } - BindWound_Struct* bind_in = (BindWound_Struct*) app->pBuffer; - Mob* bindmob = entity_list.GetMob(bind_in->to); - if (!bindmob){ - LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); - } - else { - LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); - BindWound(bindmob, true); - } - return; -} - -void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) -{ - int PlayerClass = GetClass(); - - if((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) - return; - - if (app->size != sizeof(TrackTarget_Struct)) - { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", - sizeof(TrackTarget_Struct), app->size); - return; - } - - TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; - - TrackingID = tts->EntityID; -} - -void Client::Handle_OP_Track(const EQApplicationPacket *app) -{ - if(GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) - return; - - if( GetSkill(SkillTracking)==0 ) - SetSkill(SkillTracking,1); - else - CheckIncreaseSkill(SkillTracking, nullptr, 15); - - if(!entity_list.MakeTrackPacket(this)) - LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); - - return; -} - -void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) -{ - // size 0 send right after OP_Track - return; -} - -void Client::Handle_0x0193(const EQApplicationPacket *app) -{ - // Not sure what this opcode does. It started being sent when OP_ClientUpdate was - // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate - // 2 bytes: 00 00 -} - -void Client::Handle_OP_ClientError(const EQApplicationPacket *app) -{ - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); - return; -} - -void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) -{ - if(IsInAGuild()) - { - SendGuildRanks(); - SendGuildMembers(); - } - return; -} - -void Client::Handle_OP_TGB(const EQApplicationPacket *app) -{ - OPTGB(app); - return; -} - -void Client::Handle_OP_Split(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Split_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); - return; - } - // The client removes the money on its own, but we have to - // update our state anyway, and make sure they had enough to begin - // with. - Split_Struct *split = (Split_Struct *)app->pBuffer; - //Per the note above, Im not exactly sure what to do on error - //to notify the client of the error... - if(!isgrouped) { - Message(13, "You can not split money if your not in a group."); - return; - } - Group *cgroup = GetGroup(); - if(cgroup == nullptr) { - //invalid group, not sure if we should say more... - Message(13, "You can not split money if your not in a group."); - return; - } - - if(!TakeMoneyFromPP(static_cast(split->copper) + - 10 * static_cast(split->silver) + - 100 * static_cast(split->gold) + - 1000 * static_cast(split->platinum))) { - Message(13, "You do not have enough money to do that split."); - return; - } - cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); - - return; - -} - -void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillSenseTraps)) - return; - - if(!p_timers.Expired(&database, pTimerSenseTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = SenseTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerSenseTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,800); - - CheckIncreaseSkill(SkillSenseTraps, nullptr); - - if (trap && trap->skill > 0) { - int uskill = GetSkill(SkillSenseTraps); - if ((MakeRandomInt(0,99) + uskill) >= (MakeRandomInt(0,99) + trap->skill*0.75)) - { - float xdif = trap->x - GetX(); - float ydif = trap->y - GetY(); - if (xdif == 0 && ydif == 0) - Message(MT_Skills,"You sense a trap right under your feet!"); - else if (xdif > 10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthWest."); - else if (xdif < -10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthEast."); - else if (ydif > 10) - Message(MT_Skills,"You sense a trap to the North."); - else if (xdif > 10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthWest."); - else if (xdif < -10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthEast."); - else if (ydif < -10) - Message(MT_Skills,"You sense a trap to the South."); - else if (xdif > 10) - Message(MT_Skills,"You sense a trap to the West."); - else - Message(MT_Skills,"You sense a trap to the East."); - trap->detected = true; - - float angle = CalculateHeadingToTarget(trap->x, trap->y); - - if(angle < 0) - angle = (256+angle); - - angle *= 2; - MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); - return; - } - } - Message(MT_Skills,"You did not find any traps nearby."); - return; -} - -void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillDisarmTraps)) - return; - - if(!p_timers.Expired(&database, pTimerDisarmTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = DisarmTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerDisarmTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,60); - if (trap && trap->detected) - { - int uskill = GetSkill(SkillDisarmTraps); - if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) - { - Message(MT_Skills,"You disarm a trap."); - trap->disarmed = true; - trap->chkarea_timer.Disable(); - trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var))*1000); - } - else - { - if(MakeRandomInt(0, 99) < 25){ - Message(MT_Skills,"You set off the trap while trying to disarm it!"); - trap->Trigger(this); - } - else{ - Message(MT_Skills,"You failed to disarm a trap."); - } - } - CheckIncreaseSkill(SkillDisarmTraps, nullptr); - return; - } - Message(MT_Skills,"You did not find any traps close enough to disarm."); - return; -} - -void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the guild tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==GUILD_TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates an item... - if(app->size != sizeof(TributeItem_Struct)) - printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeItem(t->slot, t->quantity); - - _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates money - if(app->size != sizeof(TributeMoney_Struct)) - printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeMoney(t->platinum); - - _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //we should enforce being near a real tribute master to change this - //but im not sure how I wanna do that right now. - if(app->size != sizeof(SelectTributeReq_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); - else { - SelectTributeReq_Struct *t = (SelectTributeReq_Struct *) app->pBuffer; - SendTributeDetails(t->client_id, t->tribute_id); - } - return; -} - -void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //sent when the client changes their tribute settings... - if(app->size != sizeof(TributeInfo_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); - else { - TributeInfo_Struct *t = (TributeInfo_Struct *) app->pBuffer; - ChangeTributeSettings(t); - } - return; -} - -void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(uint32)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); - else { - uint32 *val = (uint32 *) app->pBuffer; - ToggleTribute(*val? true : false); - } - return; -} - -void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - return; -} - -void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ControlBoat_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); - return; - } - ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; - Mob* boat = entity_list.GetMob(cbs->boatId); - if (boat == 0) - return; // do nothing if the boat isn't valid - - if(!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - if (cbs->TakeControl) { - // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. - if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { - boat->SetTarget(this); - } - else { - this->Message_StringID(13,IN_USE); - return; - } - } - else - boat->SetTarget(0); - - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ControlBoat,0); - FastQueuePacket(&outapp); - safe_delete(outapp); - // have the boat signal itself, so quests can be triggered by boat use - boat->CastToNPC()->SignalNPC(0); -} - -void Client::Handle_OP_DumpName(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) -{ - if(HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... - CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up -} - -void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_Ignore(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) -{ - if(app->size != sizeof(FindPersonRequest_Struct)) - printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n",sizeof(FindPersonRequest_Struct),app->size); - else { - FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - - std::vector points; - Mob* target = entity_list.GetMob(t->npc_id); - - if(target == nullptr) { - //empty length packet == not found. - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - if(!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || - target->CastToClient()->Buyer)) { - Message(15, "Moving you to Trader %s", target->GetName()); - MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ() , 0.0f); - } - - if(!RuleB(Pathing, Find) || !zone->pathing) - { - //fill in the path array... - // - points.resize(2); - points[0].x = GetX(); - points[0].y = GetY(); - points[0].z = GetZ(); - points[1].x = target->GetX(); - points[1].y = target->GetY(); - points[1].z = target->GetZ(); - } - else - { - Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); - Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - - if(!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) - { - points.resize(2); - points[0].x = Start.x; - points[0].y = Start.y; - points[0].z = Start.z; - - points[1].x = End.x; - points[1].y = End.y; - points[1].z = End.z; - - } - else - { - std::list pathlist = zone->pathing->FindRoute(Start, End); - - if(pathlist.size() == 0) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - //the client seems to have issues with packets larger than this - if(pathlist.size() > 36) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - // Live appears to send the points in this order: - // Final destination. - // Current Position. - // rest of the points. - FindPerson_Point p; - - int PointNumber = 0; - - bool LeadsToTeleporter = false; - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); - - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - - p.x = GetX(); - p.y = GetY(); - p.z = GetZ(); - points.push_back(p); - - for(std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) - { - if((*Iterator) == -1) // Teleporter - { - LeadsToTeleporter = true; - break; - } - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - ++PointNumber; - } - - if(!LeadsToTeleporter) - { - p.x = target->GetX(); - p.y = target->GetY(); - p.z = target->GetZ(); - - points.push_back(p); - } - - } - } - - SendPathPacket(points); - } - return; -} - -/* Finish client connecting state */ -void Client::CompleteConnect() { - UpdateWho(); - client_state = CLIENT_CONNECTED; - - hpupdate_timer.Start(); - position_timer.Start(); - autosave_timer.Start(); - SetDuelTarget(0); - SetDueling(false); - - EnteringMessages(this); - LoadZoneFlags(); - - /* Sets GM Flag if needed & Sends Petition Queue */ - UpdateAdmin(false); - - if (IsInAGuild()){ - uint8 rank = GuildRank(); - if(GetClientVersion() >= EQClientRoF) - { - switch (rank) { - case 0: { rank = 5; break; } // GUILD_MEMBER 0 - case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AT_GuildID, GuildID(), false); - SendAppearancePacket(AT_GuildRank, rank, false); - } - for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { - if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) - m_pp.spell_book[spellInt] = 0xFFFFFFFF; - } - //SendAATable(); - - if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); - - uint32 raidid = database.GetRaidID(GetName()); - Raid *raid = nullptr; - if (raidid > 0){ - raid = entity_list.GetRaidByID(raidid); - if (!raid){ - raid = new Raid(raidid); - if (raid->GetID() != 0){ - entity_list.AddRaid(raid, raidid); - } - else - raid = nullptr; - } - if (raid){ - SetRaidGrouped(true); - raid->LearnMembers(); - raid->VerifyRaid(); - raid->GetRaidDetails(); - /* - Only leader should get this; send to all for now till - I figure out correct creation; can probably also send a no longer leader packet for non leaders - but not important for now. - */ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->SendRaidAdd(GetName(), this); - raid->SendBulkRaid(this); - raid->SendGroupUpdate(this); - uint32 grpID = raid->GetGroup(GetName()); - if (grpID < 12){ - raid->SendRaidGroupRemove(GetName(), grpID); - raid->SendRaidGroupAdd(GetName(), grpID); - } - if (raid->IsLocked()) - raid->SendRaidLockTo(this); - } - } - - //bulk raid send in here eventually - - //reapply some buffs - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 j1 = 0; j1 < buff_count; j1++) { - if (!IsValidSpell(buffs[j1].spellid)) - continue; - - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; - - int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); - if(NimbusEffect) { - if(!IsNimbusEffectActive(NimbusEffect)) - SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); - } - - for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { - switch (spell.effectid[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (spell.base[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base[x1] == -2) - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); - } - else if (spell.max[x1] > 0) - { - SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); - } - else - { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); - } - switch (spell.base[x1]){ - case OGRE: - SendAppearancePacket(AT_Size, 9); - break; - case TROLL: - SendAppearancePacket(AT_Size, 8); - break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AT_Size, 7); - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AT_Size, 5); - break; - case DWARF: - SendAppearancePacket(AT_Size, 4); - break; - case HALFLING: - case GNOME: - SendAppearancePacket(AT_Size, 3); - break; - default: - SendAppearancePacket(AT_Size, 6); - break; - } - break; - } - case SE_SummonHorse: { - SummonHorse(buffs[j1].spellid); - //hasmount = true; //this was false, is that the correct thing? - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AT_Invis, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) - { - if (!GetGM()) - { - SendAppearancePacket(AT_Levitate, 0); - BuffFadeByEffect(SE_Levitate); - Message(13, "You can't levitate in this zone."); - } - } - else{ - SendAppearancePacket(AT_Levitate, 2); - } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - } - } - } - - /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ - entity_list.SendZoneAppearance(this); - /* Sends the Nimbus particle effects (up to 3) for any mob using them */ - entity_list.SendNimbusEffects(this); - - entity_list.SendUntargetable(this); - - int x; - for (x = 0; x < 8; x++) - SendWearChange(x); - Mob *pet = GetPet(); - if (pet != nullptr) { - for (x = 0; x < 8; x++) - pet->SendWearChange(x); - } - - entity_list.SendTraders(this); - - zoneinpacket_timer.Start(); - - if (GetPet()){ - GetPet()->SendPetBuffsToClient(); - } - - if (GetGroup()) - database.RefreshGroupFromDB(this); - - if (RuleB(TaskSystem, EnableTaskSystem)) - TaskPeriodic_Timer.Start(); - else - TaskPeriodic_Timer.Disable(); - - conn_state = ClientConnectFinished; - - //enforce some rules.. - if (!CanBeInZone()) { - _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); - GoToSafeCoords(database.GetZoneID("arena"), 0); - return; - } - - if (zone) - zone->weatherSend(); - - TotalKarma = database.GetKarma(AccountID()); - SendDisciplineTimers(); - - parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); - - /* This sub event is for if a player logs in for the first time since entering world. */ - if (firstlogon == 1){ - parse->EventPlayer(EVENT_CONNECT, this, "", 0); - /* QS: PlayerLogConnectDisconnect */ - if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ - std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); - } - } - - if(zone) { - if(zone->GetInstanceTimer()) { - uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); - uint32 day = (ttime/86400000); - uint32 hour = (ttime/3600000)%24; - uint32 minute = (ttime/60000)%60; - uint32 second = (ttime/1000)%60; - if(day) { - Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); - } - else if(hour) { - Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); - } - else if(minute) { - Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second); - } - else { - Message(15, "%s(%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second); - } - } - } - - SendRewards(); - SendAltCurrencies(); - database.LoadAltCurrencyValues(CharacterID(), alternate_currency); - SendAlternateCurrencyValues(); - alternate_currency_loaded = true; - ProcessAlternateCurrencyQueue(); - - /* This needs to be set, this determines whether or not data was loaded properly before a save */ - client_data_loaded = true; - - CalcItemScale(); - DoItemEnterZone(); - - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - - if(GetClientVersion() >= EQClientSoD) - entity_list.SendFindableNPCList(this); - - if(IsInAGuild()) { - SendGuildRanks(); - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); - guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); - } - - /** Request adventure info **/ - ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); - strcpy((char*)pack->pBuffer, GetName()); - worldserver.SendPacket(pack); - delete pack; - - if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } - - entity_list.RefreshClientXTargets(this); - - worldserver.RequestTellQueue(GetName()); -} - -void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) -{ - KeyRingList(); -} - -void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) { - if(app->size != 1) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint8 *mode = (uint8 *) app->pBuffer; - if(*mode) { - m_pp.leadAAActive = 1; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); - } else { - m_pp.leadAAActive = 0; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); - } -} - - -void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) { - if(app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint32 aaid = *((uint32 *) app->pBuffer); - - if(aaid >= _maxLeaderAA) - return; - - uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; - if(current_rank >= MAX_LEADERSHIP_TIERS) { - Message(13, "This ability can be trained no further."); - return; - } - - uint8 cost = LeadershipAACosts[aaid][current_rank]; - if(cost == 0) { - Message(13, "This ability can be trained no further."); - return; - } - - //TODO: we need to enforce prerequisits - - if(aaid >= raidAAMarkNPC) { - //it is a raid ability. - if(cost > m_pp.raid_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.raid_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - } else { - //it is a group ability. - if(cost > m_pp.group_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.group_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - - database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); - } - - //success, send them an update - EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); - UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *) outapp->pBuffer; - u->ability_id = aaid; - u->new_rank = m_pp.leader_abilities.ranks[aaid]; - u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities - FastQueuePacket(&outapp); - - Group *g = GetGroup(); - - // Update all group members with the new AA the leader has purchased. - if(g) { - g->UpdateGroupAAs(); - g->SendLeadershipAAUpdate(); - } - -} - -void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetTitle_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); - DumpPacket(app); - return; - } - - SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; - - std::string Title; - - if(!sts->is_suffix) - { - Title = title_manager.GetPrefix(sts->title_id); - SetAATitle(Title.c_str()); - } - else - { - Title = title_manager.GetSuffix(sts->title_id); - SetTitleSuffix(Title.c_str()); - } -} - -void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) -{ - - EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); - - if(outapp != nullptr) - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) -{ - if(app->size != sizeof(BankerChange_Struct) && app->size!=4) //Titanium only sends 4 Bytes for this - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); - DumpPacket(app); - return; - } - - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - EQApplicationPacket *outapp=new EQApplicationPacket(OP_BankerChange,nullptr,sizeof(BankerChange_Struct)); - BankerChange_Struct *bc=(BankerChange_Struct *)outapp->pBuffer; - - if(m_pp.platinum < 0) - m_pp.platinum = 0; - if(m_pp.gold < 0) - m_pp.gold = 0; - if(m_pp.silver < 0) - m_pp.silver = 0; - if(m_pp.copper < 0) - m_pp.copper = 0; - - if(m_pp.platinum_bank < 0) - m_pp.platinum_bank = 0; - if(m_pp.gold_bank < 0) - m_pp.gold_bank = 0; - if(m_pp.silver_bank < 0) - m_pp.silver_bank = 0; - if(m_pp.copper_bank < 0) - m_pp.copper_bank = 0; - - uint64 cp = static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000); - - m_pp.copper=cp%10; - cp/=10; - m_pp.silver=cp%10; - cp/=10; - m_pp.gold=cp%10; - cp/=10; - m_pp.platinum=cp; - - cp = static_cast(m_pp.copper_bank) + - (static_cast(m_pp.silver_bank) * 10) + - (static_cast(m_pp.gold_bank) * 100) + - (static_cast(m_pp.platinum_bank) * 1000); - - m_pp.copper_bank=cp%10; - cp/=10; - m_pp.silver_bank=cp%10; - cp/=10; - m_pp.gold_bank=cp%10; - cp/=10; - m_pp.platinum_bank=cp; - - bc->copper=m_pp.copper; - bc->silver=m_pp.silver; - bc->gold=m_pp.gold; - bc->platinum=m_pp.platinum; - - bc->copper_bank=m_pp.copper_bank; - bc->silver_bank=m_pp.silver_bank; - bc->gold_bank=m_pp.gold_bank; - bc->platinum_bank=m_pp.platinum_bank; - - FastQueuePacket(&outapp); - - return; -} - -void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) -{ - if(app->size != sizeof(bool)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); - DumpPacket(app); - return; - } - bool *af = (bool*)app->pBuffer; - auto_fire = *af; - auto_attack = false; - SetAttackTimer(); -} -void Client::Handle_OP_Rewind(const EQApplicationPacket *app) -{ - if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { - Message_StringID(MT_System, REWIND_WAIT); - } else { - CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); - rewind_timer.Start(30000, true); - } -} - -void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RaidGeneral_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); - DumpPacket(app); - return; - } - - RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; - switch(ri->action) - { - case RaidCommandInviteIntoExisting: - case RaidCommandInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - Group *g = i->GetGroup(); - if(g){ - if(g->IsLeader(i) == false) - Message(13, "You can only invite an ungrouped player or group leader to join your raid."); - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - break; - } - case RaidCommandAcceptInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - if(IsRaidGrouped()){ - i->Message_StringID(0, 5060); //group failed, must invite members not in raid... - return; - } - Raid *r = entity_list.GetRaidByClient(i); - if(r){ - r->VerifyRaid(); - Group *g = GetGroup(); - if(g){ - if(g->GroupCount()+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - else{ - if(1+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - if(g){//add us all - uint32 freeGroup = r->GetFreeGroup(); - Client *addClient = nullptr; - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - - if(!addClient) - { - addClient = c; - r->SetGroupLeader(addClient->GetName()); - } - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - if(g->IsLeader(g->members[x])) - r->AddMember(c, freeGroup, false, true); - else - r->AddMember(c, freeGroup); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - g->DisbandGroup(); - r->GroupUpdate(freeGroup); - } - else{ - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(this); - r->SendBulkRaid(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - else - { - Group *ig = i->GetGroup(); - Group *g = GetGroup(); - if(g) //if our target has a group - { - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - - uint32 groupFree = r->GetFreeGroup(); //get a free group - if(ig){ //if we already have a group then cycle through adding us... - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]){ - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])){ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else{ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - ig->DisbandGroup(); - r->GroupUpdate(groupFree); - groupFree = r->GetFreeGroup(); - } - else{ //else just add the inviter - r->SendRaidCreate(i); - r->AddMember(i,0xFFFFFFFF, true, false, true); - } - - Client *addClient = nullptr; - //now add the existing group - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - if(!addClient) - { - if(g->members[x]->IsClient()){ - addClient = g->members[x]->CastToClient(); - r->SetGroupLeader(addClient->GetName()); - } - } - if(g->IsLeader(g->members[x])) - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, false, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - g->DisbandGroup(); - r->GroupUpdate(groupFree); - } - else - { - if(ig){ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]) - { - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])) - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->SendBulkRaid(this); - r->AddMember(this); - ig->DisbandGroup(); - r->GroupUpdate(0); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - else{ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - r->SendRaidCreate(i); - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(i,0xFFFFFFFF, true, false, true); - r->SendBulkRaid(this); - r->AddMember(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - } - } - break; - } - case RaidCommandDisband: { - Raid *r = entity_list.GetRaidByClient(this); - if(r){ - //if(this == r->GetLeader()){ - uint32 grp = r->GetGroup(ri->leader_name); - - if(grp < 12){ - uint32 i = r->GetPlayerIndex(ri->leader_name); - if(r->members[i].IsGroupLeader){ //assign group leader to someone else - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && i != x){ - if(r->members[x].GroupNumber == grp){ - r->SetGroupLeader(ri->leader_name, false); - r->SetGroupLeader(r->members[x].membername); - break; - } - } - } - - } - if(r->members[i].IsRaidLeader){ - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) - { - r->SetRaidLeader(r->members[i].membername, r->members[x].membername); - break; - } - } - } - } - - r->RemoveMember(ri->leader_name); - Client *c = entity_list.GetClientByName(ri->leader_name); - if(c) - r->SendGroupDisband(c); - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, grp); - r->GroupUpdate(grp);// break - //} - } - break; - } - case RaidCommandMoveGroup: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(ri->parameter < 12) //moving to a group - { - uint8 grpcount = r->GroupCount(ri->parameter); - - if(grpcount < 6) - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. - break; - - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) - { - r->SetGroupLeader(ri->leader_name, false); - if(oldgrp < 12){ //we were the leader of our old grp - for(int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can - { - if(r->members[x].GroupNumber == oldgrp) - { - if(strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername, r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - } - } - if(grpcount == 0) - r->SetGroupLeader(ri->leader_name); - - r->MoveMember(ri->leader_name, ri->parameter); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - //r->SendGroupUpdate(c); - //break - r->GroupUpdate(ri->parameter); //send group update to our new group - if(oldgrp < 12) //if our old was a group send update there too - r->GroupUpdate(oldgrp); - - //r->SendMakeGroupLeaderPacketAll(); - } - } - else //moving to ungrouped - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ - r->SetGroupLeader(ri->leader_name, false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername,r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - r->MoveMember(ri->leader_name, 0xFFFFFFFF); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - r->GroupUpdate(oldgrp); - //r->SendMakeGroupLeaderPacketAll(); - } - } - break; - } - case RaidCommandRaidLock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(!r->IsLocked()) - r->LockRaid(true); - else - r->SendRaidLockTo(this); - } - break; - } - case RaidCommandRaidUnlock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(r->IsLocked()) - r->LockRaid(false); - else - r->SendRaidUnlockTo(this); - } - break; - } - case RaidCommandLootType2: - case RaidCommandLootType: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Loot type changed to: %d.", ri->parameter); - r->ChangeLootType(ri->parameter); - } - break; - } - - case RaidCommandAddLooter2: - case RaidCommandAddLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Adding %s as a raid looter.", ri->leader_name); - r->AddRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandRemoveLooter2: - case RaidCommandRemoveLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Removing %s as a raid looter.", ri->leader_name); - r->RemoveRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandMakeLeader: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(strcmp(r->leadername, GetName()) == 0){ - r->SetRaidLeader(GetName(), ri->leader_name); - } - } - break; - } - - default: { - Message(13, "Raid command (%d) NYI", ri->action); - break; - } - } -} - -void Client::Handle_OP_Translocate(const EQApplicationPacket *app) { - - if(app->size != sizeof(Translocate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); - DumpPacket(app); - return; - } - Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; - - if(!PendingTranslocate) return; - - if((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { - Message(13, "You did not accept the Translocate within the required time limit."); - PendingTranslocate = false; - return; - } - - if(its->Complete == 1) { - - int SpellID = PendingTranslocateData.SpellID; - int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); - - if(i == 0) - { - // If the spell has a translocate to bind effect, AND we are already in the zone the client - // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself - // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are - // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. - if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && - zone->GetZoneID() == PendingTranslocateData.ZoneID) - { - PendingTranslocate = false; - GoToBind(); - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); - Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; - memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); - - //Was sending the packet back to initiate client zone... - //but that could be abusable, so lets go through proper channels - MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); - } - } - - PendingTranslocate = false; -} - -void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) { - - if(app->size != sizeof(Sacrifice_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); - DumpPacket(app); - return; - } - Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; - - if(!PendingSacrifice) { - LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); - DumpPacket(app); - return; - } - - if(ss->Confirm) { - Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); - if(Caster) Sacrifice(Caster); - } - PendingSacrifice = false; - SacrificeCaster.clear(); -} - -void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(AcceptNewTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", - sizeof(AcceptNewTask_Struct), app->size); - DumpPacket(app); - return; - } - AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; - - if(ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); -} - -void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(CancelTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", - sizeof(CancelTask_Struct), app->size); - DumpPacket(app); - return; - } - CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->CancelTask(this, cts->SequenceNumber); -} - -void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(TaskHistoryRequest_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", - sizeof(TaskHistoryRequest_Struct), app->size); - DumpPacket(app); - return; - } - TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->SendTaskHistory(this, ths->TaskIndex); -} - -void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) { - - // Although there are three different structs for OP_Bandolier, they are all the same size. - // - if(app->size != sizeof(BandolierCreate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", - sizeof(BandolierCreate_Struct), app->size); - DumpPacket(app); - return; - } - - BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; - - switch(bs->action) { - case BandolierCreate: - CreateBandolier(app); - break; - case BandolierRemove: - RemoveBandolier(app); - break; - case BandolierSet: - SetBandolier(app); - break; - default: - LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); - - } -} - -void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { - - if(app->size != sizeof(PopupResponse_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", - sizeof(PopupResponse_Struct), app->size); - DumpPacket(app); - return; - } - PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; - - // Handle any EQEmu defined popup Ids first - switch(prs->popupid) - { - case POPUPID_UPDATE_SHOWSTATSWINDOW: - if(GetTarget() && GetTarget()->IsClient()) - GetTarget()->CastToClient()->SendStatsWindow(this, true); - else - SendStatsWindow(this, true); - return; - - default: - break; - } - - char buf[16]; - sprintf(buf, "%d\0", prs->popupid); - - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); - - Mob* Target = GetTarget(); - if(Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); - } -} - -void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { - if(app->size != sizeof(MovePotionToBelt_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", - sizeof(MovePotionToBelt_Struct), app->size); - DumpPacket(app); - return; - } - MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; - if(mptbs->Action == 0) { - const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); - if(BaseItem) { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; - strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); - database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); - } - } - else { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; - strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); - } -} - -void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); - ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->QuerierLevel = GetLevel(); - strcpy(smrs->FromName, GetName()); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->Classes = gmrs->Classes; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - - -void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFP_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); - DumpPacket(app); - return; - } - LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; - - LFP = lfp->Action != LFPOff; - database.SetLFP(CharacterID(), LFP); - - if(!LFP) { - worldserver.StopLFP(CharacterID()); - return; - } - - GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; - - for(unsigned int i=0; iGetZoneID(); - LFPMembers[0].GuildID = GuildID(); - - if(g) { - // This should not happen. The client checks if you are in a group and will not let you put LFP on if - // you are not the leader. - if(!g->IsLeader(this)) { - LogFile->write(EQEMuLog::Error,"Client sent LFP on for character %s who is grouped but not leader.", GetName()); - return; - } - // Fill the LFPMembers array with the rest of the group members, excluding ourself - // We don't fill in the class, level or zone, because we may not be able to determine - // them if the other group members are not in this zone. World will fill in this information - // for us, if it can. - int NextFreeSlot = 1; - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(strcasecmp(g->membername[i], LFPMembers[0].Name)) - strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); - } - } - - - worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, - lfp->Comments, LFPMembers); - - -} - -void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); - ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->QuerierLevel = GetLevel(); - smrs->QuerierClass = GetClass(); - strcpy(smrs->FromName, GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - - return; -} - -void Client::Handle_OP_Barter(const EQApplicationPacket *app) -{ - - if(app->size < 4) - { - LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); - DumpPacket(app); - return; - } - - char* Buf = (char *)app->pBuffer; - - // The first 4 bytes of the packet determine the action. A lot of Barter packets require the - // packet the client sent, sent back to it as an acknowledgement. - // - uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); - - _pkt(TRADING__BARTER, app); - - switch(Action) - { - - case Barter_BuyerSearch: - { - BuyerItemSearch(app); - break; - } - - case Barter_SellerSearch: - { - BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; - SendBuyerResults(bsr->SearchString, bsr->SearchID); - break; - } - - case Barter_BuyerModeOn: - { - if(!Trader) { - ToggleBuyerMode(true); - } - else { - Buf = (char *)app->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); - Message(13, "You cannot be a Trader and Buyer at the same time."); - } - QueuePacket(app); - break; - } - - case Barter_BuyerModeOff: - { - QueuePacket(app); - ToggleBuyerMode(false); - break; - } - - case Barter_BuyerItemUpdate: - { - UpdateBuyLine(app); - break; - } - - case Barter_BuyerItemRemove: - { - BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; - database.RemoveBuyLine(CharacterID(), bris->BuySlot); - QueuePacket(app); - break; - } - - case Barter_SellItem: - { - SellToBuyer(app); - break; - } - - case Barter_BuyerInspectBegin: - { - ShowBuyLines(app); - break; - } - - case Barter_BuyerInspectEnd: - { - BuyerInspectRequest_Struct* bir = ( BuyerInspectRequest_Struct*)app->pBuffer; - Client *Buyer = entity_list.GetClientByID(bir->BuyerID); - if(Buyer) - Buyer->WithCustomer(0); - - break; - } - - case Barter_BarterItemInspect: - { - BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Welcome: - { - SendBazaarWelcome(); - break; - } - - case Barter_WelcomeMessageUpdate: - { - BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; - SetBuyerWelcomeMessage(bwmu->WelcomeMessage); - break; - } - - case Barter_BuyerItemInspect: - { - BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Unknown23: - { - // Sent by SoD client for no discernible reason. - break; - } - - default: - Message(13, "Unrecognised Barter action."); - _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); - - } -} - -void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) { - - if(app->size != sizeof(VoiceMacroIn_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", - sizeof(VoiceMacroIn_Struct), app->size); - - DumpPacket(app); - - return; - } - - if(!RuleB(Chat, EnableVoiceMacros)) return; - - VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; - - VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); - -} - -void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DoGroupLeadershipAbility_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", - sizeof(DoGroupLeadershipAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; - - switch(dglas->Ability) - { - case GroupLeadershipAbility_MarkNPC: - { - if(GetTarget()) - { - Group* g = GetGroup(); - if(g) - g->MarkNPC(GetTarget(), dglas->Parameter); - } - break; - } - - case groupAAInspectBuffs: - { - Mob *Target = GetTarget(); - - if(!Target || !Target->IsClient()) - return; - - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return; - - Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); - - break; - } - - default: - break; - } -} - -void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) { - - if(app->size != 0) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", - app->size); - - DumpPacket(app); - - return; - } - - Group *g = GetGroup(); - - if(g) - g->ClearAllNPCMarks(); -} - -void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DelegateAbility_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", - sizeof(DelegateAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) return; - - switch(das->DelegateAbility) - { - case 0: - { - g->DelegateMainAssist(das->Name); - break; - } - case 1: - { - g->DelegateMarkNPC(das->Name); - break; - } - case 2: - { - g->DelegateMainTank(das->Name); - break; - } - case 3: - { - g->DelegatePuller(das->Name); - break; - } - default: - break; - } -} - -void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { - if (app->size != sizeof(ApplyPoison_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); - DumpPacket(app); - return; - } - uint32 ApplyPoisonSuccessResult = 0; - ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; - const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); - const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); - const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - - if(!IsPoison) - { - mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); - } - else if(GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { - float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; - double ChanceRoll = MakeRandomFloat(0, 1); - - CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); - - if(ChanceRoll < SuccessChance) { - ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX()/100) + 103); - } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); - ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; - ApplyPoisonResult->success = ApplyPoisonSuccessResult; - ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; - - FastQueuePacket(&outapp); -} - - -void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - - // This packet is sent by the client when an Augment item information window is opened. - // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. - // The OP_Augment packet includes a window parameter to determine which Item window in the UI the - // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. - // - - if(app->size != sizeof(AugmentInfo_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", - sizeof(AugmentInfo_Struct), app->size); - - DumpPacket(app); - - return; - } - AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*) app->pBuffer; - - char *outstring = nullptr; - - const Item_Struct * item = database.GetItem(AugInfo->itemid); - - if (item) - { - MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - - BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; - - out->window = AugInfo->window; - - out->type = 2; - - out->invslot = 0; - - strcpy(out->booktext, outstring); - - safe_delete_array(outstring); - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) -{ - // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. - // - // It has a single uint32 payload which is the sort method: - // - // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 - // - if(app->size != sizeof(PVPLeaderBoardRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", - sizeof(PVPLeaderBoardRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); - /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) -{ - // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends - // further details about the selected player, e.g. Race/Class/AAs/Guild etc. - // - if(app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", - sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); - PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Adventure_Sell_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", - app->size, sizeof(Adventure_Sell_Struct)); - DumpPacket(app); - return; - } - - Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(ams_in->npcid); - if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && - (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) - { - Message(13, "Vendor was not found."); - return; - } - - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - { - Message(13, "Vendor is out of range."); - return; - } - - uint32 itemid = GetItemIDAt(ams_in->slot); - - if(itemid == 0) - { - Message(13, "Found no item at that slot."); - return; - } - - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(ams_in->slot); - if(!item || !inst){ - Message(13, "You seemed to have misplaced that item..."); - return; - } - - // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor - // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the - // items. - // - // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, - // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting - // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for - // it, but he will refuse for item 76053. - // - // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. - // - // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle - // that case here. - if(item->LDoNSold == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if(item->LDoNPrice == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - int32 price = item->LDoNPrice * 70 / 100; - - if(price == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, ams_in->charges, price, item, false); - - if(!inst->IsStackable()) - { - DeleteItemInInventory(ams_in->slot, 0, false); - } - else - { - if(inst->GetCharges() < ams_in->charges) - { - ams_in->charges = inst->GetCharges(); - } - - if(ams_in->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } - - DeleteItemInInventory(ams_in->slot, ams_in->charges, false); - price *= ams_in->charges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); - Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; - ams->slot = ams_in->slot; - ams->unknown000 = 1; - ams->npcid = ams->npcid; - ams->charges = ams_in->charges; - ams->sell_price = price; - FastQueuePacket(&outapp); - - switch(vendor->GetClass()) - { - case ADVENTUREMERCHANT: - { - UpdateLDoNPoints(price, 6); - break; - } - case NORRATHS_KEEPERS_MERCHANT: - { - SetRadiantCrystals(GetRadiantCrystals() + price); - break; - } - case DARK_REIGN_MERCHANT: - { - SetEbonCrystals(GetEbonCrystals() + price); - break; - } - - default: - break; - } - - Save(1); -} - -void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) -{ - if(adventure_stats_timer) - { - return; - } - - adventure_stats_timer = new Timer(8000); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); - AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; - - if(database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, - as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) - { - as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; - as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; - m_pp.ldon_wins_guk = as->success.guk; - m_pp.ldon_wins_mir = as->success.mir; - m_pp.ldon_wins_mmc = as->success.mmc; - m_pp.ldon_wins_ruj = as->success.ruj; - m_pp.ldon_wins_tak = as->success.tak; - m_pp.ldon_losses_guk = as->failure.guk; - m_pp.ldon_losses_mir = as->failure.mir; - m_pp.ldon_losses_mmc = as->failure.mmc; - m_pp.ldon_losses_ruj = as->failure.ruj; - m_pp.ldon_losses_tak = as->failure.tak; - } - - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureLeaderboardRequest_Struct)) - { - return; - } - - if(adventure_leaderboard_timer) - { - return; - } - - adventure_leaderboard_timer = new Timer(4000); - ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); - ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; - strcpy(lr->player, GetName()); - - AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; - lr->type = 1 + (lrs->theme * 2) + lrs->type; - worldserver.SendPacket(pack); - delete pack; -} - -void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) -{ -// This opcode is sent by the client when the player choses which bind to return to. -// The client sends just a 4 byte packet with the selection number in it -// - if(app->size != 4) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", - 4, app->size); - DumpPacket(app); - return; - } - char *Buffer = (char *)app->pBuffer; - - uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - HandleRespawnFromHover(Option); -} - void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) { - if(app->size != sizeof(GroupUpdate_Struct)) + if (app->size != sizeof(GroupUpdate_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_GroupUpdate: got %u expected %u", app->size, sizeof(GroupUpdate_Struct)); @@ -11469,285 +6873,39 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) GroupUpdate_Struct* gu = (GroupUpdate_Struct*)app->pBuffer; - switch(gu->action) { - case groupActMakeLeader: - { - Mob* newleader = entity_list.GetClientByName(gu->membername[0]); - Group* group = this->GetGroup(); + switch (gu->action) { + case groupActMakeLeader: + { + Mob* newleader = entity_list.GetClientByName(gu->membername[0]); + Group* group = this->GetGroup(); - if (newleader && group) { - // the client only sends this if it's the group leader, but check anyway - if(group->IsLeader(this)) - group->ChangeLeader(newleader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s",GetName()); - DumpPacket(app); - } + if (newleader && group) { + // the client only sends this if it's the group leader, but check anyway + if (group->IsLeader(this)) + group->ChangeLeader(newleader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - - default: - { - LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); - DumpPacket(app); - return; - } - } -} - -void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) -{ - // if the character has a start city, don't let them use the command - if(m_pp.binds[4].zoneId != 0) { - Message(15,"Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); - return; + break; } - if (app->size < 1) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + default: + { + LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); DumpPacket(app); return; } - - float x(0),y(0),z(0); - uint32 zoneid = 0; - uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - - std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); - return; } - - bool validCity = false; - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - if(zoneid != startCity) - continue; - - validCity = true; - x = atof(row[2]); - y = atof(row[3]); - z = atof(row[4]); - } - - if(validCity) { - Message(15,"Your home city has been set"); - SetStartZone(startCity, x, y, z); - return; - } - - query = StringFormat("SELECT zone_id, bind_id FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - - Message(15,"Use \"/startcity #\" to choose a home city from the following list:"); - - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - char* name; - database.GetZoneLongName(database.GetZoneName(zoneid), &name); - Message(15,"%d - %s", zoneid, name); - } - -} - -void Client::Handle_OP_Report(const EQApplicationPacket *app) -{ - if(!CanUseReport) - { - Message_StringID(MT_System, REPORT_ONCE); - return; - } - - uint32 size = app->size; - uint32 current_point = 0; - std::string reported, reporter; - std::string current_string; - int mode = 0; - - while(current_point < size) - { - if(mode < 2) - { - if(app->pBuffer[current_point] == '|') - { - mode++; - } - else - { - if(mode == 0) - { - reported += app->pBuffer[current_point]; - } - else - { - reporter += app->pBuffer[current_point]; - } - } - current_point++; - } - else - { - if(app->pBuffer[current_point] == 0x0a) - { - current_string += '\n'; - } - else if(app->pBuffer[current_point] == 0x00) - { - CanUseReport = false; - database.AddReport(reporter, reported, current_string); - return; - } - else - { - current_string += app->pBuffer[current_point]; - } - current_point++; - } - } - - CanUseReport = false; - database.AddReport(reporter, reported, current_string); -} - -void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(VeteranClaimRequest)) - { - LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", - app->size, sizeof(VeteranClaimRequest)); - DumpPacket(app); - return; - } - - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; - - if(vcr->claim_id == 0xFFFFFFFF) //request update packet - { - SendRewards(); - } - else //try to claim something! - { - if(!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } -} - -void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) -{ - // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can - // be displayed in the window, including all the HTML formatting tags. - // - const int maxResults = 10; - - if(app->size < sizeof(GMSearchCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", - app->size, sizeof(GMSearchCorpse_Struct)); - DumpPacket(app); - return; - } - - GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; - gmscs->Name[63] = '\0'; - - char *escSearchString = new char[129]; - database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); - - std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " - "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", - escSearchString, maxResults); - safe_delete_array(escSearchString); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); - return; - } - - if (results.RowCount() == 0) - return; - - if(results.RowCount() == maxResults) - Message(clientMessageError, "Your search found too many results; some are not displayed."); - else - Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); - - char charName[64], timeOfDeath[20]; - - std::string popupText = ""; - - for (auto row = results.begin(); row != results.end(); ++row) { - - strn0cpy(charName, row[0], sizeof(charName)); - - uint32 ZoneID = atoi(row[1]); - float CorpseX = atof(row[2]); - float CorpseY = atof(row[3]); - float CorpseZ = atof(row[4]); - - strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); - - bool corpseRezzed = atoi(row[6]); - bool corpseBuried = atoi(row[7]); - - popupText += StringFormat("", - charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, - corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); - - if(popupText.size() > 4000) { - Message(clientMessageError, "Unable to display all the results."); - break; - } - - } - - popupText += "
NameZoneXYZDate" - "RezzedBuried
 " - "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; - - SendPopupToClient("Corpses", popupText.c_str()); - } void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) { - if(!GuildBanks) + if (!GuildBanks) return; - if((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) + if ((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) { Message(13, "The Guild Bank is not available in this zone."); @@ -11764,11 +6922,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - if(!IsInAGuild()) + if (!IsInAGuild()) { Message(13, "You must be in a Guild to use the Guild Bank."); - if(Action == GuildBankDeposit) + if (Action == GuildBankDeposit) GuildBankDepositAck(true); else GuildBankAck(); @@ -11776,9 +6934,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) return; } - if(!IsGuildBanker()) + if (!IsGuildBanker()) { - if((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) + if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) { _log(GUILDS__BANK_ERROR, "Suspected hacking attempt on guild bank from %s", GetName()); @@ -11788,700 +6946,253 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) } } - switch(Action) + switch (Action) { - case GuildBankPromote: + case GuildBankPromote: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - { - Message_StringID(13, GUILD_BANK_FULL); + Message_StringID(13, GUILD_BANK_FULL); - GuildBankDepositAck(true); + GuildBankDepositAck(true); - return; - } - - GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - - int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - - if(Slot >= 0) - { - ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - - if(inst) - { - Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); - safe_delete(inst); - } - } - else - Message(13, "Unexpected error while moving item into Guild Bank."); - - GuildBankAck(); - - break; - } - - case GuildBankViewItem: - { - GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); - - if(!inst) - break; - - SendItemPacket(0, inst, ItemPacketViewLink); - - safe_delete(inst); - - break; - } - - case GuildBankDeposit: // Deposit Item - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) - { - Message_StringID(13, GUILD_BANK_FULL); - - GuildBankDepositAck(true); - - return; - } - - ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); - - bool Allowed = true; - - if(!CursorItemInst) - { - Message(13, "No Item on the cursor."); - - GuildBankDepositAck(true); - - return; - } - - const Item_Struct* CursorItem = CursorItemInst->GetItem(); - - if(!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsNoneEmptyContainer()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsAugmented()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->NoRent == 0) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - - if(!Allowed) - { - GuildBankDepositAck(true); - - return; - } - - if(GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) - { - GuildBankDepositAck(false); - - DeleteItemInInventory(MainCursor, 0, false); - } - - break; - } - - case GuildBankPermissions: - { - GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; - - if(gbps->Permissions == 1) - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); - else - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); - - GuildBankAck(); - break; - } - - case GuildBankWithdraw: - { - if (GetInv()[MainCursor]) - { - Message_StringID(13, GUILD_BANK_EMPTY_HANDS); - - GuildBankAck(); - - break; - } - - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - - if(!inst) - { - GuildBankAck(); - - break; - } - - if(!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) - { - _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if(CheckLoreConflict(inst->GetItem())) - { - Message_StringID(13, DUP_LORE); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if (gbwis->Quantity > 0) - { - PushItemOnCursor(*inst); - - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - - GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - } - else - { - Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); - } - - safe_delete(inst); - - GuildBankAck(); - - break; - } - - case GuildBankSplitStacks: - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - Message_StringID(13, GUILD_BANK_FULL); - else - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); - } - - GuildBankAck(); - - break; - } - - case GuildBankMergeStacks: - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); - - GuildBankAck(); - - break; - } - - default: - { - Message(13, "Unexpected GuildBank action."); - - _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); - } - } -} - -void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupRole_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); - DumpPacket(app); - return; - } - GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) - return; - - switch(grs->RoleNumber) - { - case 1: //Main Tank - { - if(grs->Toggle) - g->DelegateMainTank(grs->Name1, grs->Toggle); - else - g->UnDelegateMainTank(grs->Name1, grs->Toggle); - break; - } - case 2: //Main Assist - { - if(grs->Toggle) - g->DelegateMainAssist(grs->Name1, grs->Toggle); - else - g->UnDelegateMainAssist(grs->Name1, grs->Toggle); - break; - } - case 3: //Puller - { - if(grs->Toggle) - g->DelegatePuller(grs->Name1, grs->Toggle); - else - g->UnDelegatePuller(grs->Name1, grs->Toggle); - break; - } - default: - break; - } -} - -void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) -{ - // New OPCode for SOD+ as /hidecorpse is handled serverside now. - // - if(app->size != sizeof(HideCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", - sizeof(HideCorpse_Struct), app->size); - - DumpPacket(app); - - return; - } - - HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; - - if(hcs->Action == HideCorpseLooted) - return; - - if((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) - return; - - entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); - - HideCorpseMode = hcs->Action; -} - -void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildUpdateURLAndChannel_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", - sizeof(GuildUpdateURLAndChannel_Struct), app->size); - - DumpPacket(app); - - return; - } - - GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; - - if(!IsInAGuild()) - return; - - if(!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can change the Channel or URL.!"); - return; - } - - if(guuacs->Action == 0) - guild_mgr.SetGuildURL(GuildID(), guuacs->Text); - else - guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); - -} - -void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildStatus_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", - sizeof(GuildStatus_Struct), app->size); - - DumpPacket(app); - - return; - } - GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(gss->Name); - - if(!c) - { - Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); - return; - } - - uint32 TargetGuildID = c->GuildID(); - - if(TargetGuildID == GUILD_NONE) - { - Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); - return; - } - - const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); - - if(!GuildName) - return; - - bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); - bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); - - if((TargetGuildID == GuildID()) && (c != this)) - { - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); - else - Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); - - return; - } - - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); - else - Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); -} - -void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - - std::set::iterator Iterator; - - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - if(bbs->Initialise == 1) - { - BlockedBuffs->clear(); - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) - { - if(BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) - BlockedBuffs->insert(bbs->SpellID[i]); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 1; - obbs->Flags = 0x54; - obbs->Count = BlockedBuffs->size(); - - unsigned int Element = 0; - - Iterator = BlockedBuffs->begin(); - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - return; - } - - if((bbs->Initialise == 0) && (bbs->Count > 0)) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x54; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if(!IsBeneficialSpell(bbs->SpellID[i])) - continue; - - if((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) - BlockedBuffs->insert(bbs->SpellID[i]); - } - obbs->Count = BlockedBuffs->size(); - - Iterator = BlockedBuffs->begin(); - - unsigned int Element = 0; - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - std::set RemovedBuffs; - - if(bbs->Count > 0) - { - std::set::iterator Iterator; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = 0; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x5a; - - for(unsigned int i = 0; i < bbs->Count; ++i) - { - Iterator = BlockedBuffs->find(bbs->SpellID[i]); - - if(Iterator != BlockedBuffs->end()) - { - RemovedBuffs.insert(bbs->SpellID[i]); - - BlockedBuffs->erase(Iterator); - } - } - obbs->Count = RemovedBuffs.size(); - - Iterator = RemovedBuffs.begin(); - - unsigned int Element = 0; - - while(Iterator != RemovedBuffs.end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} -void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); - - DumpPacket(app); - - return; - } - - bool Pet = app->pBuffer[0]; - - if(Pet) - PetBlockedBuffs.clear(); - else - PlayerBlockedBuffs.clear(); - - QueuePacket(app); -} - -void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) -{ - // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets - // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. - // - VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); - - BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; - - Mob *m = nullptr; - - if(brrs->EntityID == GetID()) - m = this; - else if(brrs->EntityID == GetPetID()) - m = GetPet(); - - if(!m) - return; - - if(brrs->SlotID > (uint32)m->GetMaxTotalSlots()) - return; - - uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - - if(SpellID && IsBeneficialSpell(SpellID)) - m->BuffFadeBySlot(brrs->SlotID, true); -} - -void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) -{ - if(DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) - { - Message_StringID(13, CORPSEDRAG_LIMIT); - return; - } - - VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); - - CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; - - Mob* corpse = entity_list.GetMob(cds->CorpseName); - - if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) - return; - - Client *c = entity_list.FindCorpseDragger(corpse->GetID()); - - if(c) - { - if(c == this) - Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); - else - Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); - - return; - } - - if(!corpse->CastToCorpse()->Summon(this, false, true)) - return; - - DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); - - Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); -} - -void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) -{ - if(app->size == 1) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); - ClearDraggedCorpses(); - return; - } - - for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) - { - if(!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); - Iterator = DraggedCorpses.erase(Iterator); return; } - } -} -void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); + GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; + int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); + if (Slot >= 0) + { + ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - Group* g = GetGroup(); - - if (NewLeader && g) - { - if(g->IsLeader(this)) - g->ChangeLeader(NewLeader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); - DumpPacket(app); + if (inst) + { + Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); + safe_delete(inst); + } } + else + Message(13, "Unexpected error while moving item into Guild Bank."); + + GuildBankAck(); + + break; + } + + case GuildBankViewItem: + { + GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); + + if (!inst) + break; + + SendItemPacket(0, inst, ItemPacketViewLink); + + safe_delete(inst); + + break; + } + + case GuildBankDeposit: // Deposit Item + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) + { + Message_StringID(13, GUILD_BANK_FULL); + + GuildBankDepositAck(true); + + return; + } + + ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); + + bool Allowed = true; + + if (!CursorItemInst) + { + Message(13, "No Item on the cursor."); + + GuildBankDepositAck(true); + + return; + } + + const Item_Struct* CursorItem = CursorItemInst->GetItem(); + + if (!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsNoneEmptyContainer()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsAugmented()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->NoRent == 0) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + + if (!Allowed) + { + GuildBankDepositAck(true); + + return; + } + + if (GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) + { + GuildBankDepositAck(false); + + DeleteItemInInventory(MainCursor, 0, false); + } + + break; + } + + case GuildBankPermissions: + { + GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; + + if (gbps->Permissions == 1) + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); + else + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); + + GuildBankAck(); + break; + } + + case GuildBankWithdraw: + { + if (GetInv()[MainCursor]) + { + Message_StringID(13, GUILD_BANK_EMPTY_HANDS); + + GuildBankAck(); + + break; + } + + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + + if (!inst) + { + GuildBankAck(); + + break; + } + + if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) + { + _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (CheckLoreConflict(inst->GetItem())) + { + Message_StringID(13, DUP_LORE); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (gbwis->Quantity > 0) + { + PushItemOnCursor(*inst); + + SendItemPacket(MainCursor, inst, ItemPacketSummonItem); + + GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + } + else + { + Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); + } + + safe_delete(inst); + + GuildBankAck(); + + break; + } + + case GuildBankSplitStacks: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) + Message_StringID(13, GUILD_BANK_FULL); + else + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); + } + + GuildBankAck(); + + break; + } + + case GuildBankMergeStacks: + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); + + GuildBankAck(); + + break; + } + + default: + { + Message(13, "Unexpected GuildBank action."); + + _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); + } } } void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) { - if(IsInAGuild()) + if (IsInAGuild()) { Message(clientMessageError, "You are already in a guild!"); return; } - if(!RuleB(Guild, PlayerCreationAllowed)) + if (!RuleB(Guild, PlayerCreationAllowed)) { Message(clientMessageError, "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild."); return; @@ -12504,19 +7215,19 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) #if __DARWIN_C_LEVEL < 200809L if (strlen(GuildName) > 60) #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // __DARWIN_C_LEVEL #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // DARWIN { Message(clientMessageError, "Guild name too long."); return; } - for(unsigned int i = 0; i < strlen(GuildName); ++i) + for (unsigned int i = 0; i < strlen(GuildName); ++i) { - if(!isalpha(GuildName[i]) && (GuildName[i] != ' ')) + if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) { Message(clientMessageError, "Invalid character in Guild name."); return; @@ -12525,13 +7236,13 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); - if(GuildCount >= RuleI(Guild, PlayerCreationLimit)) + if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) { Message(clientMessageError, "You cannot create this guild because this account may only be leader of %i guilds.", RuleI(Guild, PlayerCreationLimit)); return; } - if(guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) + if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) { Message_StringID(clientMessageError, GUILD_NAME_IN_USE); return; @@ -12546,820 +7257,1047 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) Message(clientMessageError, "Guild creation failed."); else { - if(!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) + if (!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) Message(clientMessageError, "Unable to set guild leader's guild in the database. Contact a GM."); else { Message(clientMessageYellow, "You are now the leader of %s", GuildName); - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) GuildBanks->SendGuildBank(this); SendGuildRanks(); } } } -void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); +void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); - bool found = false; - while(altc_iter != zone->AlternateCurrencies.end()) { - if((*altc_iter).id == alt_cur_id) { - found = true; - break; - } - ++altc_iter; - } - - if(!found) { - return; - } - - std::stringstream ss(std::stringstream::in | std::stringstream::out); - std::stringstream item_ss(std::stringstream::in | std::stringstream::out); - ss << alt_cur_id << "|1|" << alt_cur_id; - uint32 count = 0; - uint32 merchant_id = tar->MerchantType; - const Item_Struct *item = nullptr; - - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - item_ss << "^" << item->Name << "|"; - item_ss << item->ID << "|"; - item_ss << ml.alt_currency_cost << "|"; - item_ss << "0|"; - item_ss << "1|"; - item_ss << item->Races << "|"; - item_ss << item->Classes; - count++; - } - } - - if(count > 0) { - ss << "|" << count << item_ss.str(); - } else { - ss << "|0"; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); - memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); - - AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - ItemInst *inst = m_inv.GetItem(select->slot_id); - if(!inst) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - - if (RuleB(Merchant, EnableAltCurrencySell)) { - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!found) { - cost = 0; - } - } + if (!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + Message(0, "You are not a guild leader or not in a guild."); + else { + mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); + if (!guild_mgr.DeleteGuild(GuildID())) + Message(0, "Guild delete failed."); else { - cost = 0; + Message(0, "Guild successfully deleted."); } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); - AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; - reply->unknown004 = 0xFF; - reply->unknown005 = 0xFF; - reply->unknown006 = 0xFF; - reply->unknown007 = 0xFF; - strcpy(reply->item_name, inst->GetItem()->Name); - reply->cost = cost; - FastQueuePacket(&outapp); } } -void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); - AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; +void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(cost > current_currency) { - Message(13, "You cannot afford that item right now."); - return; - } - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - - /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - - AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - - Save(1); - } -} - -void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); - AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; - uint32 item_id = 0; - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - if((*iter).id == reclaim->currency_id) { - item_id = (*iter).item_id; - } - ++iter; - } - - if(item_id == 0) { + if (app->size != sizeof(GuildDemoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildDemoteStruct)); return; } - /* Item to Currency Storage */ - if(reclaim->reclaim_flag == 1) { - uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); - if(removed > 0) { - AddAlternateCurrencyValue(reclaim->currency_id, removed); + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(demote->target, gci)) { + Message(0, "Unable to find '%s'", demote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (gci.rank < 1) { + Message(0, "%s cannot be demoted any further!", demote->target); + return; + } + uint8 rank = gci.rank - 1; + + + mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + demote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); + return; + } + Message(0, "Successfully demoted %s to rank %d", demote->target, rank); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + + if (!IsInAGuild()) + Message(0, "Error: You are not in a guild!"); + else if (gc->officer > GUILD_MAX_RANK) + Message(13, "Invalid rank."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + + //ok, the invite is also used for changing rank as well. + Mob* invitee = entity_list.GetMob(gc->othername); + + if (!invitee) { + Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); + return; + } + + if (invitee->IsClient()) { + Client* client = invitee->CastToClient(); + + //ok, figure out what they are trying to do. + if (client->GuildID() == GuildID()) { + //they are already in this guild, must be a promotion or demotion + if (gc->officer < client->GuildRank()) { + //demotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + //we could send this to the member and prompt them to see if they want to + //be demoted (I guess), but I dont see a point in that. + + mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { + Message(13, "There was an error during the demotion, DB may now be inconsistent."); + return; + } + + } + else if (gc->officer > client->GuildRank()) { + //promotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the promotion with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->QueuePacket(app); + + } + else { + Message(13, "That member is already that rank."); + return; + } + } + else if (!client->IsInAGuild()) { + //they are not in this or any other guild, this is an invite + // + if (client->GetPendingGuildInvitation()) + { + Message(13, "That person is already considering a guild invitation."); + return; + } + + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { + Message(13, "You dont have permission to invite."); + return; + } + + mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the invite with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->SetPendingGuildInvitation(true); + client->QueuePacket(app); + + } + else { + //they are in some other guild + Message(13, "Player is in a guild."); + return; } } +#ifdef BOTS + else if (invitee->IsBot()) { + // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system + Bot::ProcessGuildInvite(this, invitee->CastToBot()); + return; + } +#endif } - /* Cursor to Item storage */ - else { - uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); +} - /* If you input more than you have currency wise, just give the max of the currency you currently have */ - if(reclaim->count > max_currency) { - SummonItem(item_id, max_currency); - SetAlternateCurrencyValue(reclaim->currency_id, 0); +void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SetPendingGuildInvitation(false); + + if (app->size != sizeof(GuildInviteAccept_Struct)) { + std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; + return; + } + + GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + + if (GetClientVersion() >= EQClientRoF) + { + if (gj->response > 9) + { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + return; + } + } + if (gj->response == 5 || gj->response == 4) { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + + return; + } + + //uint32 tmpeq = gj->guildeqid; + if (IsInAGuild() && gj->response == GuildRank()) + Message(0, "Error: You're already in a guild!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", + gj->guildeqid, gj->response, gj->inviter, gj->newmember); + + //we dont really care a lot about what this packet means, as long as + //it has been authorized with the guild manager + if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); + Message(13, "Invalid invite response packet!"); + return; + } + + if (gj->guildeqid == GuildID()) { + //only need to change rank. + + mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + gj->response, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { + Message(13, "There was an error during the rank change, DB may now be inconsistent."); + return; + } } else { - SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); - AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); - } - /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + + mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", + GetName(), CharacterID(), + guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, + gj->response); + + //change guild and rank + + uint32 guildrank = gj->response; + + if (GetClientVersion() == EQClientRoF) + { + if (gj->response == 8) + { + guildrank = 0; + } + } + + if (!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { + Message(13, "There was an error during the invite, DB may now be inconsistent."); + return; + } + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); } } } -void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); - EQApplicationPacket *outapp = app->Copy(); - AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; +void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; + if (app->size < 2) { + mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); + return; + } - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } + app->pBuffer[app->size - 1] = 0; + GuildMakeLeader* gml = (GuildMakeLeader*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (GuildRank() != GUILD_LEADER) + Message(0, "Error: You arent the guild leader!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } + //NOTE: we could do cross-zone lookups here... - ItemInst* inst = GetInv().GetItem(sell->slot_id); - if(!inst) { - return; - } + Client* newleader = entity_list.GetClientByName(gml->target); + if (newleader) { - if (!RuleB(Merchant, EnableAltCurrencySell)) { - return; - } + mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", + guild_mgr.GetGuildName(GuildID()), GuildID(), + newleader->GetName(), newleader->CharacterID()); - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - uint32 npc_id = tar->GetNPCTypeID(); - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; + if (guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ + Message(0, "Successfully Transfered Leadership to %s.", gml->target); + newleader->Message(15, "%s has transfered the guild leadership into your hands.", GetName()); } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if(!found) { - return; - } - - if(!inst->IsStackable()) - { - DeleteItemInInventory(sell->slot_id, 0, false); + else + Message(0, "Could not change leadership at this time."); } else - { - if(inst->GetCharges() < sell->charges) - { - sell->charges = inst->GetCharges(); - } + Message(0, "Failed to change leader, could not find target."); + } + // SendGuildMembers(GuildID(), true); + return; +} - if(sell->charges == 0) - { - Message(13, "Charge mismatch error."); +void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) +{ + + mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + if (app->size != sizeof(GuildManageBanker_Struct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); + return; + } + GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*)app->pBuffer; + + if (!IsInAGuild()) { + Message(13, "Your not in a guild!"); + return; + } + + CharGuildInfo gci; + + if (!guild_mgr.GetCharInfo(gmb->member, gci)) + { + Message(0, "Unable to find '%s'", gmb->member); + return; + } + bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); + + bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); + + bool NewBankerStatus = gmb->enabled & 0x01; + + bool NewAltStatus = gmb->enabled & 0x02; + + if ((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can assign guild bankers!"); + return; + } + + if (IsCurrentlyAnAlt != NewAltStatus) + { + bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); + + if (!IsAllowed) + { + Message(13, "You are not allowed to change the alt status of %s", gmb->member); + return; + } + } + + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (IsCurrentlyABanker != NewBankerStatus) + { + if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { + Message(13, "Error setting guild banker flag."); + return; + } + + if (NewBankerStatus) + Message(0, "%s has been made a guild banker.", gmb->member); + else + Message(0, "%s is no longer a guild banker.", gmb->member); + } + if (IsCurrentlyAnAlt != NewAltStatus) + { + if (!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { + Message(13, "Error setting guild alt flag."); + return; + } + + if (NewAltStatus) + Message(0, "%s has been marked as an alt.", gmb->member); + else + Message(0, "%s is no longer marked as an alt.", gmb->member); + } +} + +void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildPromoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildPromoteStruct)); + return; + } + + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(promote->target, gci)) { + Message(0, "Unable to find '%s'", promote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + uint8 rank = gci.rank + 1; + + if (rank > GUILD_OFFICER) + return; + + + mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + promote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); + return; + } + Message(0, "Successfully promoted %s to rank %d", promote->target, rank); + } + return; +} + +void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size < sizeof(GuildUpdate_PublicNote)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n", app->size, sizeof(GuildUpdate_PublicNote)); + return; + } + GuildUpdate_PublicNote* gpn = (GuildUpdate_PublicNote*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gpn->target, gci)) { + Message(0, "Unable to find '%s'", gpn->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", + gpn->target, gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID(), + gpn->note); + + if (!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { + Message(13, "Failed to set public note on %s", gpn->target); + } + else { + Message(0, "Successfully changed public note on %s", gpn->target); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + // we can always remove ourself, otherwise, our rank needs remove permissions + else if (strcasecmp(gc->othername, GetName()) != 0 && + !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) + Message(0, "You dont have permission to remove guild members."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { +#ifdef BOTS + if (Bot::ProcessGuildRemoval(this, gc->othername)) + return; +#endif + uint32 char_id; + Client* client = entity_list.GetClientByName(gc->othername); + + if (client) { + if (!client->IsInGuild(GuildID())) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); return; } + char_id = client->CharacterID(); - DeleteItemInInventory(sell->slot_id, sell->charges, false); - cost *= sell->charges; + mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + } + else { + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gc->othername, gci)) { + Message(0, "Unable to find '%s'", gc->othername); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + char_id = gci.char_id; + + mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", + gci.char_name.c_str(), gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID()); } - sell->cost = cost; - - /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + if (!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); + GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*)outapp->pBuffer; + gm->guildeqid = GuildID(); + strcpy(gm->member, gc->othername); + Message(0, "%s successfully removed from your guild.", gc->othername); + entity_list.QueueClientsGuild(this, outapp, false, GuildID()); + safe_delete(outapp); } + else + Message(0, "Unable to remove %s from your guild.", gc->othername); + } + // SendGuildMembers(GuildID(), true); + return; +} +void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildStatus_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", + sizeof(GuildStatus_Struct), app->size); + + DumpPacket(app); + + return; + } + GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(gss->Name); + + if (!c) + { + Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); + return; + } + + uint32 TargetGuildID = c->GuildID(); + + if (TargetGuildID == GUILD_NONE) + { + Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); + return; + } + + const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); + + if (!GuildName) + return; + + bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); + bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); + + if ((TargetGuildID == GuildID()) && (c != this)) + { + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); + else + Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); + + return; + } + + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); + else + Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); +} + +void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", + sizeof(GuildUpdateURLAndChannel_Struct), app->size); + + DumpPacket(app); + + return; + } + + GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; + + if (!IsInAGuild()) + return; + + if (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can change the Channel or URL.!"); + return; + } + + if (guuacs->Action == 0) + guild_mgr.SetGuildURL(GuildID(), guuacs->Text); + else + guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); + +} + +void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Hide(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + //Can not be able to train hide but still have it from racial though + return; //You cannot hide if you do not have hide + } + + if (!p_timers.Expired(&database, pTimerHide, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = HideReuseTime - GetAA(209); + p_timers.Start(pTimerHide, reuse - 1); + + float hidechance = ((GetSkill(SkillHide) / 250.0f) + .25) * 100; + float random = MakeRandomFloat(0, 100); + CheckIncreaseSkill(SkillHide, nullptr, 5); + if (random < hidechance) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 1; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetAA(aaShroudofStealth)){ + improved_hidden = true; + hidden = true; + } + else + hidden = true; + } + if (GetClass() == ROGUE){ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + Mob *evadetar = GetTarget(); + if (!auto_attack && (evadetar && evadetar->CheckAggro(this) + && evadetar->IsNPC())) { + if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { + msg->string_id = EVADE_SUCCESS; + RogueEvade(evadetar); + } + else { + msg->string_id = EVADE_FAIL; + } + } + else { + if (hidden){ + msg->string_id = HIDE_SUCCESS; + } + else { + msg->string_id = HIDE_FAIL; + } + } FastQueuePacket(&outapp); - AddAlternateCurrencyValue(alt_cur_id, cost); - Save(1); } + return; } -void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) { - uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - if((ebon + radiant) > 0) { - AddCrystals(radiant, ebon); - } -} - -void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); - CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; - - if(cr->type == 5) { - if(cr->amount > GetEbonCrystals()) { - SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); - m_pp.currentEbonCrystals = 0; - m_pp.careerEbonCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); - m_pp.currentEbonCrystals -= cr->amount; - m_pp.careerEbonCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } else if(cr->type == 4) { - if(cr->amount > GetRadiantCrystals()) { - SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); - m_pp.currentRadCrystals = 0; - m_pp.careerRadCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); - m_pp.currentRadCrystals -= cr->amount; - m_pp.careerRadCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } -} - -void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) { - if(app->size < 4) + // New OPCode for SOD+ as /hidecorpse is handled serverside now. + // + if (app->size != sizeof(HideCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", + sizeof(HideCorpse_Struct), app->size); + + DumpPacket(app); + + return; + } + + HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; + + if (hcs->Action == HideCorpseLooted) return; - uint32 Command = *((uint32 *) app->pBuffer); + if ((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) + return; - switch(Command) - { - case 0: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); - LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(pts->Comment) > 256) -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); - pack->WriteUInt32(GetBaseClass()); - pack->WriteUInt32(GetLevel()); - pack->WriteUInt32(GetAAPointsSpent()); - pack->WriteString(pts->Comment); - pack->WriteUInt32(pts->Toggle); - pack->WriteUInt32(pts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 1: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); - LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; - -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(gts->Comment) > 256) -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); - pack->WriteString(guild_mgr.GetGuildName(GuildID())); - pack->WriteString(gts->Comment); - pack->WriteUInt32(gts->FromLevel); - pack->WriteUInt32(gts->ToLevel); - pack->WriteUInt32(gts->Classes); - pack->WriteUInt32(gts->AACount); - pack->WriteUInt32(gts->Toggle); - pack->WriteUInt32(gts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 3: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_PlayerMatches); - - LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; - pack->WriteUInt32(sps->FromLevel); - pack->WriteUInt32(sps->ToLevel); - pack->WriteUInt32(sps->MinAA); - pack->WriteUInt32(sps->TimeZone); - pack->WriteUInt32(sps->Classes); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 4: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_GuildMatches); - - LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; - - pack->WriteUInt32(sgs->Level); - pack->WriteUInt32(sgs->AAPoints); - pack->WriteUInt32(sgs->TimeZone); - pack->WriteUInt32(sgs->Class); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - default: - break; - } + HideCorpseMode = hcs->Action; } -void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +void Client::Handle_OP_Ignore(const EQApplicationPacket *app) { - if(app->size < 12) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + return; +} + +void Client::Handle_OP_Illusion(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Illusion_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, + sizeof(Illusion_Struct)); DumpPacket(app); return; } - uint32 Unknown000 = app->ReadUInt32(0); - - if(Unknown000 != 1) - return; - - uint32 Slot = app->ReadUInt32(4); - - if(Slot >= XTARGET_HARDCAP) - return; - - XTargetType Type = (XTargetType)app->ReadUInt32(8); - - XTargets[Slot].Type = Type; - XTargets[Slot].ID = 0; - XTargets[Slot].Name[0] = 0; - - switch(Type) + if (!GetGM()) { - case Empty: - case Auto: - { - break; - } - - case CurrentTargetPC: - { - char Name[65]; - - app->ReadString(Name, 12, 64); - Client *c = entity_list.GetClientByName(Name); - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, Name, 64); - } - SendXTargetPacket(Slot, c); - - break; - } - - case CurrentTargetNPC: - { - char Name[65]; - app->ReadString(Name, 12, 64); - Mob *m = entity_list.GetMob(Name); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - break; - } - } - - case TargetsTarget: - { - if(GetTarget()) - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - else - UpdateXTargetType(TargetsTarget, nullptr); - - break; - } - - case GroupTank: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainTankName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - case GroupTankTarget: - { - Group *g = GetGroup(); - - if(g) - g->NotifyTankTarget(this); - - break; - } - - case GroupAssist: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainAssistName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case GroupAssistTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyAssistTarget(this); - - break; - } - - case Puller: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetPullerName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case PullerTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyPullerTarget(this); - - break; - } - - case GroupMarkTarget1: - case GroupMarkTarget2: - case GroupMarkTarget3: - { - Group *g = GetGroup(); - - if(g) - g->SendMarkedNPCsToMember(this); - - break; - } - - case RaidAssist1: - case RaidAssist2: - case RaidAssist3: - case RaidAssist1Target: - case RaidAssist2Target: - case RaidAssist3Target: - case RaidMarkTarget1: - case RaidMarkTarget2: - case RaidMarkTarget3: - { - // Not implemented yet. - break; - } - - case MyPet: - { - Mob *m = GetPet(); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - case MyPetTarget: - { - Mob *m = GetPet(); - - if(m) - m = m->GetTarget(); - - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - - default: - LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); - break; + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); + return; } + Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; + //these need to be implemented + /* + texture = bnpc->texture; + helmtexture = bnpc->helmtexture; + luclinface = bnpc->luclinface; + */ + race = bnpc->race; + size = 0; + + entity_list.QueueClients(this, app); + return; } -void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + + if (app->size != sizeof(InspectResponse_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); + return; + } + + //Fills the app sent from client. + EQApplicationPacket* outapp = app->Copy(); + InspectResponse_Struct* insr = (InspectResponse_Struct*)outapp->pBuffer; + Mob* tmp = entity_list.GetMob(insr->TargetID); + const Item_Struct* item = nullptr; + + for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { + const ItemInst* inst = GetInv().GetItem(L); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } + else { insr->itemicons[L] = 0xFFFFFFFF; } + } + + const ItemInst* inst = GetInv().GetItem(MainAmmo); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + // another one..I did these, didn't I!!? + strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); + insr->itemicons[SoF::slots::MainAmmo] = item->Icon; + } + else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)insr->text; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); + + if (tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester + + return; +} + +void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(InspectMessage_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); + return; + } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)app->pBuffer; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); +} + +void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Inspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); + return; + } + + Inspect_Struct* ins = (Inspect_Struct*)app->pBuffer; + Mob* tmp = entity_list.GetMob(ins->TargetID); + + if (tmp != 0 && tmp->IsClient()) { + if (tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target + // Inspecting an SoF or later client will make the server handle the request + else { ProcessInspectRequest(tmp->CastToClient(), this); } + } + +#ifdef BOTS + if (tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } +#endif + + return; +} + +void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) +{ + //packet is empty as of 12/14/04 + + if (!p_timers.Expired(&database, pTimerInstillDoubt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime - 1); + + InstillDoubt(GetTarget()); + return; +} + +void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemViewRequest_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); DumpPacket(app); return; } - XTargetAutoAddHaters = app->ReadUInt8(0); + DumpPacket(app); + ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + + //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + + const Item_Struct* item = database.GetItem(ivrs->item_id); + if (!item) { + if (ivrs->item_id > 500000) + { + std::string response = ""; + int sayid = ivrs->item_id - 500000; + bool silentsaylink = false; + + if (sayid > 250000) //Silent Saylink + { + sayid = sayid - 250000; + silentsaylink = true; + } + + if (sayid > 0) + { + + std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + if (results.RowCount() != 1) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + auto row = results.begin(); + response = row[0]; + + } + + if ((response).size() > 0) + { + if (!mod_saylink(response, silentsaylink)) { return; } + + if (GetTarget() && GetTarget()->IsNPC()) + { + if (silentsaylink) + { + parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + else + { + if (silentsaylink) + { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + } + else + { + Message(13, "Error: Say Link not found or is too long."); + return; + } + } + else { + Message(13, "Error: The item for the link you have clicked on does not exist!"); + return; + } + + } + + ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LDONItemViewRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + return; + } + LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; + ItemInst* inst = database.CreateItem(item->item_id); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemNamePacket_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", + sizeof(ItemNamePacket_Struct), app->size); + return; + } + ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; + const Item_Struct *item = 0; + if ((item = database.GetItem(p->item_id)) != nullptr) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemName, sizeof(ItemNamePacket_Struct)); + p = (ItemNamePacket_Struct*)outapp->pBuffer; + memset(p, 0, sizeof(ItemNamePacket_Struct)); + strcpy(p->name, item->Name); + FastQueuePacket(&outapp); + } + return; } void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) @@ -13535,14 +8473,983 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - } else + } + else return; } +void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemVerifyRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + return; + } + + ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; + int32 slot_id; + int32 target_id; + int32 spell_id = 0; + slot_id = request->slot; + target_id = request->target; + + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); + ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; + reply->slot = slot_id; + reply->target = target_id; + + QueuePacket(outapp); + safe_delete(outapp); + + + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + return; + } + + if (slot_id < 0) { + LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i", GetName(), request->slot); + return; + } + + const ItemInst* inst = m_inv[slot_id]; + if (!inst) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + const Item_Struct* item = inst->GetItem(); + if (!item) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + spell_id = item->Click.Effect; + + if + ( + spell_id > 0 && + ( + !IsValidSpell(spell_id) || + casting_spell_id || + delaytimer || + spellend_timer.Enabled() || + IsStunned() || + IsFeared() || + IsMezzed() || + DivineAura() || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) || + (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) + ) + ) + { + SendSpellBarEnable(spell_id); + return; + } + + LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); + + if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check + { + ItemInst* p_inst = (ItemInst*)inst; + + parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + int r; + bool tryaug = false; + ItemInst* clickaug = 0; + Item_Struct* augitem = 0; + + for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { + const ItemInst* aug_i = inst->GetAugment(r); + if (!aug_i) + continue; + const Item_Struct* aug = aug_i->GetItem(); + if (!aug) + continue; + + if ((aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2)) + { + tryaug = true; + clickaug = (ItemInst*)aug_i; + augitem = (Item_Struct*)aug; + spell_id = aug->Click.Effect; + break; + } + } + + if ((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) + { + LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s", GetName()); + } + else if (inst->IsType(ItemClassCommon)) + { + if (item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) + { + DeleteItemInInventory(slot_id, 1, true); + TrainDiscipline(item->ID); + } + else if (item->ItemType == ItemTypeSpell) + { + return; + } + else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (inst->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= item->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else if (tryaug) + { + if (clickaug->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= augitem->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else + { + if (GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(), GetClass())) + { + if (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + else + { + //This is food/drink - consume it + if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeAlcohol) + { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); +#endif + // This Seems to be handled in OP_DeleteItem handling + //DeleteItemInInventory(slot_id, 1, false); + //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); + //Should add intoxication level to the PP at some point + //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); + } + + if (m_pp.hunger_level > 6000) + m_pp.hunger_level = 6000; + if (m_pp.thirst_level > 6000) + m_pp.thirst_level = 6000; + + EQApplicationPacket *outapp2; + outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; + sta->food = m_pp.hunger_level; + sta->water = m_pp.thirst_level; + + QueuePacket(outapp2); + safe_delete(outapp2); + } + + } + else + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + } + } + else + { + Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); + } + + return; +} + +void Client::Handle_OP_Jump(const EQApplicationPacket *app) +{ + SetEndurance(GetEndurance() - (GetLevel()<20 ? (225 * GetLevel() / 100) : 50)); + return; +} + +void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) +{ + KeyRingList(); +} + +void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) +{ + if (app->size < sizeof(bool)) + { + return; + } + + if (GetPendingAdventureCreate()) + { + return; + } + + if (IsOnAdventure()) + { + return; + } + + bool* p = (bool*)app->pBuffer; + if (*p == true) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct)+(64 * adv_requested_member_count)); + ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; + strcpy(sac->leader, GetName()); + sac->id = adv_requested_id; + sac->theme = adv_requested_theme; + sac->member_count = adv_requested_member_count; + memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); + pack->Deflate(); + worldserver.SendPacket(pack); + delete pack; + PendingAdventureCreate(); + ClearPendingAdventureData(); + } + else + { + ClearPendingAdventureData(); + } +} + +void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillDisarmTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the disarm trap skill."); + } +} + +void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->GetClass() == LDON_TREASURE) + Message(15, "%s", target->GetCleanName()); +} + +void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->IsNPC()) + HandleLDoNOpen(target->CastToNPC()); +} + +void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillPickLock)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); + } + else + Message(13, "You do not have the pick locks skill."); + } +} + +void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillSenseTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the sense traps skill."); + } +} + +void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) +{ + if (app->size != 1) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint8 *mode = (uint8 *)app->pBuffer; + if (*mode) { + m_pp.leadAAActive = 1; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); + } + else { + m_pp.leadAAActive = 0; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); + } +} + +void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) +{ + if (!IsOnAdventure()) + { + return; + } + LeaveAdventure(); +} + +void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) +{ + Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id + if (boat) { + if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) + boat->SetTarget(0); // fix it to stop later problems + } + this->BoatID = 0; + return; +} + +void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LFG_Struct)) { + std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; + DumpPacket(app); + return; + } + + // Process incoming packet + LFG_Struct* lfg = (LFG_Struct*)app->pBuffer; + + switch (lfg->value & 0xFF) { + case 0: + if (LFG) { + database.SetLFG(CharacterID(), false); + LFG = false; + LFGComments[0] = '\0'; + } + break; + case 1: + if (!LFG) { + LFG = true; + database.SetLFG(CharacterID(), true); + } + LFGFromLevel = lfg->FromLevel; + LFGToLevel = lfg->ToLevel; + LFGMatchFilter = lfg->MatchFilter; + strcpy(LFGComments, lfg->Comments); + break; + default: + Message(0, "Error: unknown LFG value %i", lfg->value); + } + + UpdateWho(); + + // Issue outgoing packet to notify other clients + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); + LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; + lfga->spawn_id = this->GetID(); + lfga->lfg = (uint8)LFG; + + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); + ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->QuerierLevel = GetLevel(); + strcpy(smrs->FromName, GetName()); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->Classes = gmrs->Classes; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +{ + if (app->size < 4) + return; + + uint32 Command = *((uint32 *)app->pBuffer); + + switch (Command) + { + case 0: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); + LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(pts->Comment) > 256) +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); + pack->WriteUInt32(GetBaseClass()); + pack->WriteUInt32(GetLevel()); + pack->WriteUInt32(GetAAPointsSpent()); + pack->WriteString(pts->Comment); + pack->WriteUInt32(pts->Toggle); + pack->WriteUInt32(pts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 1: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); + LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(gts->Comment) > 256) +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); + pack->WriteString(guild_mgr.GetGuildName(GuildID())); + pack->WriteString(gts->Comment); + pack->WriteUInt32(gts->FromLevel); + pack->WriteUInt32(gts->ToLevel); + pack->WriteUInt32(gts->Classes); + pack->WriteUInt32(gts->AACount); + pack->WriteUInt32(gts->Toggle); + pack->WriteUInt32(gts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 3: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_PlayerMatches); + + LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; + pack->WriteUInt32(sps->FromLevel); + pack->WriteUInt32(sps->ToLevel); + pack->WriteUInt32(sps->MinAA); + pack->WriteUInt32(sps->TimeZone); + pack->WriteUInt32(sps->Classes); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 4: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_GuildMatches); + + LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; + + pack->WriteUInt32(sgs->Level); + pack->WriteUInt32(sgs->AAPoints); + pack->WriteUInt32(sgs->TimeZone); + pack->WriteUInt32(sgs->Class); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + default: + break; + } +} + +void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFP_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); + DumpPacket(app); + return; + } + LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; + + LFP = lfp->Action != LFPOff; + database.SetLFP(CharacterID(), LFP); + + if (!LFP) { + worldserver.StopLFP(CharacterID()); + return; + } + + GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; + + for (unsigned int i = 0; iGetZoneID(); + LFPMembers[0].GuildID = GuildID(); + + if (g) { + // This should not happen. The client checks if you are in a group and will not let you put LFP on if + // you are not the leader. + if (!g->IsLeader(this)) { + LogFile->write(EQEMuLog::Error, "Client sent LFP on for character %s who is grouped but not leader.", GetName()); + return; + } + // Fill the LFPMembers array with the rest of the group members, excluding ourself + // We don't fill in the class, level or zone, because we may not be able to determine + // them if the other group members are not in this zone. World will fill in this information + // for us, if it can. + int NextFreeSlot = 1; + for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (strcasecmp(g->membername[i], LFPMembers[0].Name)) + strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); + } + } + + + worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, + lfp->Comments, LFPMembers); + + +} + +void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); + ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->QuerierLevel = GetLevel(); + smrs->QuerierClass = GetClass(); + strcpy(smrs->FromName, GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + + return; +} + +void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LoadSpellSet_Struct)) { + printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n", sizeof(LoadSpellSet_Struct), app->size); + return; + } + int i; + LoadSpellSet_Struct* ss = (LoadSpellSet_Struct*)app->pBuffer; + for (i = 0; ispell[i] != 0xFFFFFFFF) + UnmemSpell(i, true); + } +} + +void Client::Handle_OP_Logout(const EQApplicationPacket *app) +{ + //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); + //we will save when we get destroyed soon anyhow + //Save(); + + SendLogoutPackets(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + Disconnect(); + return; +} + +void Client::Handle_OP_LootItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LootingItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); + return; + } + /* + ** fixed the looting code so that it sends the correct opcodes + ** and now correctly removes the looted item the player selected + ** as well as gives the player the proper item. + ** Also fixed a few UI lock ups that would occur. + */ + + EQApplicationPacket* outapp = 0; + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); + outapp = new EQApplicationPacket(OP_LootComplete, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + if (entity->IsCorpse()) { + entity->CastToCorpse()->LootItem(this, app); + return; + } + else { + Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); + Corpse::SendEndLootErrorPacket(this); + } + + return; +} + +void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + + SetLooting(true); + + Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); + if (ent == 0) { + Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); + Corpse::SendLootReqErrorPacket(this); + return; + } + if (ent->IsCorpse()) + { + Corpse *ent_corpse = ent->CastToCorpse(); + if (DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) + { + Message(13, "Corpse too far away."); + Corpse::SendLootReqErrorPacket(this); + return; + } + + if (invisible) { + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if (invisible_undead) { + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if (invisible_animals){ + BuffFadeByEffect(SE_InvisVsAnimals); + invisible_animals = false; + } + if (hidden || improved_hidden){ + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + ent->CastToCorpse()->MakeLootRequestPackets(this, app); + return; + } + else { + std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; + Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); + Corpse::SendLootReqErrorPacket(this); + } + return; +} + +void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // i think thats the sign to stop the songs + if (IsBardSong(casting_spell_id) || bardsong != 0) + InterruptSpell(SONG_ENDS, 0x121); + else + InterruptSpell(INTERRUPT_SPELL, 0x121); + + return; + } + else // I don't think the client sends proper manachanges + { // with a length, just the 0 len ones for stopping songs + //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; + printf("OP_ManaChange from client:\n"); + DumpPacket(app); + } + return; +} + +/* +#if 0 // I dont think there's an op for this now, and we check this +// when the client is sitting +void Client::Handle_OP_Medding(const EQApplicationPacket *app) +{ + if (app->pBuffer[0]) + medding = true; + else + medding = false; + return; +} +#endif +*/ + +void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) +{ + OPMemorizeSpell(app); + return; +} + +void Client::Handle_OP_Mend(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillMend)) + return; + + if (!p_timers.Expired(&database, pTimerMend, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerMend, MendReuseTime - 1); + + int mendhp = GetMaxHP() / 4; + int currenthp = GetHP(); + if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { + + int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; + + if (MakeRandomInt(0, 99) < criticalchance){ + mendhp *= 2; + Message_StringID(4, MEND_CRITICAL); + } + SetHP(GetHP() + mendhp); + SendHPUpdate(); + Message_StringID(4, MEND_SUCCESS); + } + else { + /* the purpose of the following is to make the chance to worsen wounds much less common, + which is more consistent with the way eq live works. + according to my math, this should result in the following probability: + 0 skill - 25% chance to worsen + 20 skill - 23% chance to worsen + 50 skill - 16% chance to worsen */ + if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend), 100) < 75) && (MakeRandomInt(1, 3) == 1)) + { + SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); + SendHPUpdate(); + Message_StringID(4, MEND_WORSEN); + } + else + Message_StringID(4, MEND_FAIL); + } + + CheckIncreaseSkill(SkillMend, nullptr, 10); + return; +} + +void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MercenaryCommand_Struct)) + { + Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + DumpPacket(app); + return; + } + + MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*)app->pBuffer; + uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) + int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); + + if (!RuleB(Mercs, AllowMercs)) + return; + + // Handle the Command here... + // Will need a list of what every type of command is supposed to do + // Unsure if there is a server response to this packet + if (option >= 0) + { + Merc* merc = GetMerc(); + GetMercInfo().State = option; + + if (merc) { + uint8 numStances = 0; + + //get number of available stances for the current merc + std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; + std::list::iterator iter = mercStanceList.begin(); + while (iter != mercStanceList.end()) { + numStances++; + ++iter; + } + + MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); + if (mercTemplate) { + + //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) + if (option >= 0 && option < numStances) { + merc->SetStance(mercTemplate->Stances[option]); + GetMercInfo().Stance = mercTemplate->Stances[option]; + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); + } + } + } + } +} + void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) { // The payload is 4 bytes. The EntityID of the Mercenary Liason which are of class 71. - if(app->size != sizeof(MercenaryMerchantShopRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantShopRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); @@ -13551,41 +9458,41 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) return; } - MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*) app->pBuffer; + MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*)app->pBuffer; uint32 merchant_id = mmsr->MercMerchantID; uint32 altCurrentType = 19; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Data Request for Merchant ID (%i)", merchant_id); //client is requesting data about currently owned mercenary - if(merchant_id == 0) { + if (merchant_id == 0) { //send info about your current merc(s) } - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } NPC* tar = entity_list.GetNPCByID(merchant_id); - if(tar) { + if (tar) { int mercTypeCount = 0; int mercCount = 0; - if(DistNoRoot(*tar) > USE_NPC_RANGE2) + if (DistNoRoot(*tar) > USE_NPC_RANGE2) return; - if(tar->GetClass() != MERCERNARY_MASTER) { + if (tar->GetClass() != MERCERNARY_MASTER) { return; } mercTypeCount = tar->GetNumMercTypes(GetClientVersion()); mercCount = tar->GetNumMercs(GetClientVersion()); - if(mercCount > MAX_MERC) - return; + if (mercCount > MAX_MERC) + return; std::list mercTypeList = tar->GetMercTypesList(GetClientVersion()); std::list mercDataList = tar->GetMercsList(GetClientVersion()); @@ -13593,10 +9500,10 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) int i = 0; int StanceCount = 0; - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) + for (std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { std::list::iterator siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); - for(siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) + for (siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) { StanceCount++; } @@ -13606,28 +9513,28 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; mml->MercTypeCount = mercTypeCount; - if(mercTypeCount > 0) + if (mercTypeCount > 0) { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type - i++; + for (std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { + mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type + i++; } } mml->MercCount = mercCount; - if(mercCount > 0) + if (mercCount > 0) { i = 0; - for(std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) + for (std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) { mml->Mercs[i].MercID = mercListIter->MercTemplateID; mml->Mercs[i].MercType = mercListIter->MercType; mml->Mercs[i].MercSubType = mercListIter->MercSubType; - mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; - mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; + mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; + mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; mml->Mercs[i].Status = 0; - mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; - mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; + mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; + mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; mml->Mercs[i].AltCurrencyType = altCurrentType; mml->Mercs[i].MercUnk01 = 0; mml->Mercs[i].TimeLeft = -1; @@ -13635,19 +9542,19 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) mml->Mercs[i].MercUnk02 = 1; int mercStanceCount = 0; std::list::iterator iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - for(iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) + for (iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) { - mercStanceCount++; + mercStanceCount++; } mml->Mercs[i].StanceCount = mercStanceCount; mml->Mercs[i].MercUnk03 = 519044964; mml->Mercs[i].MercUnk04 = 1; //mml->Mercs[i].MercName; int stanceindex = 0; - if(mercStanceCount > 0) + if (mercStanceCount > 0) { std::list::iterator iter2 = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - while(iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) + while (iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) { mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; mml->Mercs[i].Stances[stanceindex].Stance = (iter2->StanceID); @@ -13662,10 +9569,65 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) } } +void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) +{ + // The payload is 0 bytes. + if (app->size != 0) + { + Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Data Update Request Received."); + + if (GetMercID()) + { + SendMercPersonalInfo(); + } +} + +void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) +{ + // The payload is 0 or 1 bytes. + if (app->size > 1) + { + Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + uint8 Command = 0; + if (app->size > 0) + { + char *InBuffer = (char *)app->pBuffer; + Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); + + // Handle the dismiss here... + if (GetMercID()) { + Merc* merc = GetMerc(); + + if (merc) { + if (CheckCanDismissMerc()) { + merc->Dismiss(); + } + } + } + + //SendMercMerchantResponsePacket(10); +} + void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) { // The payload is 16 bytes. First four bytes are the Merc ID (Template ID) - if(app->size != sizeof(MercenaryMerchantRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); @@ -13674,32 +9636,32 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) return; } - MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*) app->pBuffer; + MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*)app->pBuffer; uint32 merc_template_id = mmrq->MercID; uint32 merchant_id = mmrq->MercMerchantID; uint32 merc_unk1 = mmrq->MercUnk01; uint32 merc_unk2 = mmrq->MercUnk02; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Template ID (%i), Merchant ID (%i), Unknown1 (%i), Unknown2 (%i)", merc_template_id, merchant_id, merc_unk1, merc_unk2); //HirePending = true; SetHoTT(0); SendTargetCommand(0); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); - if(merc_template) { + if (merc_template) { Mob* merchant = entity_list.GetNPCByID(merchant_id); - if(!CheckCanHireMerc(merchant, merc_template_id)) { + if (!CheckCanHireMerc(merchant, merc_template_id)) { return; } - if(RuleB(Mercs, ChargeMercPurchaseCost)) { + if (RuleB(Mercs, ChargeMercPurchaseCost)) { uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold TakeMoneyFromPP(cost, true); } @@ -13710,7 +9672,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - if(merc) { + if (merc) { SpawnMerc(merc, true); merc->Save(); @@ -13730,7 +9692,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) { - if(app->size != sizeof(SuspendMercenary_Struct)) + if (app->size != sizeof(SuspendMercenary_Struct)) { Message(13, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); @@ -13738,133 +9700,23 @@ void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) return; } - SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*) app->pBuffer; + SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*)app->pBuffer; uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; // Check if the merc is suspended and if so, unsuspend, otherwise suspend it SuspendMercCommand(); } -void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MercenaryCommand_Struct)) - { - Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - DumpPacket(app); - return; - } - - MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*) app->pBuffer; - uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) - int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); - - if(!RuleB(Mercs, AllowMercs)) - return; - - // Handle the Command here... - // Will need a list of what every type of command is supposed to do - // Unsure if there is a server response to this packet - if(option >= 0) - { - Merc* merc = GetMerc(); - GetMercInfo().State = option; - - if(merc) { - uint8 numStances = 0; - - //get number of available stances for the current merc - std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; - std::list::iterator iter = mercStanceList.begin(); - while(iter != mercStanceList.end()) { - numStances++; - ++iter; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); - if(mercTemplate) { - - //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) - if(option >= 0 && option < numStances) { - merc->SetStance(mercTemplate->Stances[option]); - GetMercInfo().Stance = mercTemplate->Stances[option]; - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); - } - } - } - } -} - -void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) -{ - // The payload is 0 bytes. - if(app->size != 0) - { - Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Data Update Request Received."); - - if(GetMercID()) - { - SendMercPersonalInfo(); - } -} - -void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) -{ - // The payload is 0 or 1 bytes. - if(app->size > 1) - { - Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - uint8 Command = 0; - if(app->size > 0) - { - char *InBuffer = (char *)app->pBuffer; - Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); - - // Handle the dismiss here... - if(GetMercID()) { - Merc* merc = GetMerc(); - - if(merc) { - if(CheckCanDismissMerc()) { - merc->Dismiss(); - } - } - } - - //SendMercMerchantResponsePacket(10); -} - void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) { // The payload is 0 bytes. - if(app->size > 1) + if (app->size > 1) { Message(13, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); @@ -13872,10 +9724,10 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) return; } - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Timer Request received."); - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } @@ -13884,30 +9736,100 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) uint32 entityID = 0; uint32 mercState = 5; uint32 suspendedTime = 0; - if(GetMercID()) { + if (GetMercID()) { Merc* merc = GetMerc(); - if(merc) { + if (merc) { entityID = merc->GetID(); - if(GetMercInfo().IsSuspended) { + if (GetMercInfo().IsSuspended) { mercState = 1; suspendedTime = GetMercInfo().SuspendedTime; } } } - if(entityID > 0) { + if (entityID > 0) { SendMercTimerPacket(entityID, mercState, suspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); } } -void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) { - // Does not exist in Ti, UF or RoF clients - // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MoveCoin_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + DumpPacket(app); + return; + } + OPMoveCoin(app); + return; } -void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { +void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) +{ + if (!CharacterID()) + { + return; + } + + if (app->size != sizeof(MoveItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); + return; + } + + MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) + { + if (mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) + { + char *detect = nullptr; + const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); + const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); + MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", + mi->from_slot, + itm_from ? itm_from->GetID() : 0, + mi->to_slot, + itm_to ? itm_to->GetID() : 0, + casting_spell_id); + database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); + safe_delete_array(detect); + Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots + return; + } + } + + // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. + bool mi_hack = false; + + if (mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 from_parent = m_inv.CalcSlotId(mi->from_slot); + if (!m_inv[from_parent]) { mi_hack = true; } + else if (!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 to_parent = m_inv.CalcSlotId(mi->to_slot); + if (!m_inv[to_parent]) { mi_hack = true; } + else if (!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } + + if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } + + return; +} + +void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) +{ // Does not exist in Ti client // SoF, SoD and UF clients send a 4-byte packet indicating the 'parent' slot // SoF, SoD and UF slots are defined by a uint32 value and currently untranslated @@ -13925,6 +9847,4208 @@ void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { // Manually looting a corpse results in a from '34' to '68' value for equipment items, '0' to '0' for inventory. } -void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) { - // handle as needed or ignore like we have been doing... +void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the guild tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == GUILD_TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; } + +void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) +{ + // Does not exist in Ti, UF or RoF clients + // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +} + +void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; +} + +void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) +{ + if (app->size < 2) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); + return; + } + if (petition_list.DeletePetitionByCharName((char*)app->pBuffer)) + Message_StringID(0, PETITION_DELETED); + else + Message_StringID(0, PETITION_NO_DELETE); + return; +} + +void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetCommand_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); + return; + } + char val1[20] = { 0 }; + PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; + Mob* mypet = this->GetPet(); + + if (!mypet || pet->command == PET_LEADER) + { + if (pet->command == PET_LEADER) + { + if (mypet && (!GetTarget() || GetTarget() == mypet)) + { + mypet->Say_StringID(PET_LEADERIS, GetName()); + } + else if ((mypet = GetTarget())) + { + Mob *Owner = mypet->GetOwner(); + if (Owner) + mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); + else + mypet->Say_StringID(I_FOLLOW_NOONE); + } + } + + return; + } + + if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) + return; + + // just let the command "/pet get lost" work for familiars + if (mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) + return; + + uint32 PetCommand = pet->command; + + // Handle Sit/Stand toggle in UF and later. + if (GetClientVersion() >= EQClientUnderfoot) + { + if (PetCommand == PET_SITDOWN) + if (mypet->GetPetOrder() == SPO_Sit) + PetCommand = PET_STANDUP; + } + + switch (PetCommand) + { + case PET_ATTACK: { + if (!GetTarget()) + break; + if (GetTarget()->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + break; + } + if (mypet->IsFeared()) + break; //prevent pet from attacking stuff while feared + + if (!mypet->IsAttackAllowed(GetTarget())) { + mypet->Say_StringID(NOT_LEGAL_TARGET); + break; + } + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + if (mypet->IsHeld()) { + if (!mypet->IsFocused()) { + mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. + if (mypet->GetPetOrder() != SPO_Guard) + mypet->SetPetOrder(SPO_Follow); + } + else { + mypet->SetTarget(GetTarget()); + } + } + zone->AddAggroMob(); + mypet->AddToHateList(GetTarget(), 1); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + } + } + break; + } + case PET_BACKOFF: { + if (mypet->IsFeared()) break; //keeps pet running while feared + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_CALMING); + mypet->WipeHateList(); + mypet->SetTarget(nullptr); + } + break; + } + case PET_HEALTHREPORT: { + Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); + mypet->ShowBuffList(this); + //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); + break; + } + case PET_GETLOST: { + if (mypet->Charmed()) + break; + if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { + // eqlive ignores this command + // we could just remove the charm + // and continue + mypet->BuffFadeByEffect(SE_Charm); + break; + } + else { + SetPet(nullptr); + } + + mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + + //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. + /* + if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + } + */ + + break; + } + case PET_GUARDHERE: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + if (mypet->IsNPC()) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); + mypet->SetPetOrder(SPO_Guard); + mypet->CastToNPC()->SaveGuardSpot(); + } + } + break; + } + case PET_FOLLOWME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_TAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_DO_TAUNT); + mypet->CastToNPC()->SetTaunting(true); + } + break; + } + case PET_NOTAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_NO_TAUNT); + mypet->CastToNPC()->SetTaunting(false); + } + break; + } + case PET_GUARDME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SITDOWN: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); + } + break; + } + case PET_STANDUP: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SLUMBER: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if (mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); + } + break; + } + case PET_HOLD: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC()){ + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_ON: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_OFF: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) + mypet->SetHeld(false); + break; + } + case PET_NOCAST: { + if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsNoCast()) { + Message_StringID(MT_PetResponse, PET_CASTING); + mypet->CastToNPC()->SetNoCast(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOT_CASTING); + mypet->CastToNPC()->SetNoCast(true); + } + } + break; + } + case PET_FOCUS: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_ON: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (!mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_OFF: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + } + break; + } + default: + printf("Client attempted to use a unknown pet command:\n"); + break; + } +} + +void Client::Handle_OP_Petition(const EQApplicationPacket *app) +{ + if (app->size <= 1) + return; + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) + { + Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); + return; + }*/ + else + { + if (petition_list.FindPetitionByAccountName(AccountName())) + { + Message(0, "You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); + return; + } + Petition* pet = new Petition(CharacterID()); + pet->SetAName(this->AccountName()); + pet->SetClass(this->GetClass()); + pet->SetLevel(this->GetLevel()); + pet->SetCName(this->GetName()); + pet->SetRace(this->GetRace()); + pet->SetLastGM(""); + pet->SetCName(this->GetName()); + pet->SetPetitionText((char*)app->pBuffer); + pet->SetZone(zone->GetZoneID()); + pet->SetUrgency(0); + petition_list.AddPetition(pet); + database.InsertPetitionToDB(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); + } + return; +} + +void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionBug_Struct)) + printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); + else{ + Message(0, "Petition Bugs are not supported, please use /bug."); + } + return; +} + +void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Petition_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); + return; + } + Petition_Struct* inpet = (Petition_Struct*)app->pBuffer; + + Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); + //if (inpet->urgency != pet->GetUrgency()) + pet->SetUrgency(inpet->urgency); + pet->SetLastGM(this->GetName()); + pet->SetGMText(inpet->gmtext); + + pet->SetCheckedOut(false); + petition_list.UpdatePetition(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->AddCheckout(); + getpet->SetCheckedOut(true); + getpet->SendPetitionToPlayer(this->CastToClient()); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionUpdate_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*)outapp->pBuffer; + pet->petnumber = *((int*)app->pBuffer); + pet->color = 0x00; + pet->status = 0xFFFFFFFF; + pet->senttime = 0; + strcpy(pet->accountid, ""); + strcpy(pet->gmsenttoo, ""); + pet->quetotal = petition_list.GetTotalPetitions(); + strcpy(pet->charname, ""); + FastQueuePacket(&outapp); + + if (petition_list.DeletePetition(pet->petnumber) == -1) + std::cout << "Something is borked with: " << pet->petnumber << std::endl; + petition_list.ClearPetitions(); + petition_list.UpdateGMQueue(); + petition_list.ReadDatabase(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) +{ +#ifdef _EQDEBUG + printf("%s looking at petitions..\n", this->GetName()); +#endif + return; +} + +void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) +{ + // This is When Client Asks for Petition Again and Again... + // break is here because it floods the zones and causes lag if it + // Were to actually do something:P We update on our own schedule now. + return; +} + +void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) +{ + Handle_OP_PetitionDelete(app); +} + +void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->SetCheckedOut(false); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PickPocket_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); + DumpPacket(app); + } + + if (!HasSkill(SkillPickPockets)) + { + return; + } + + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); + return; + } + PickPocket_Struct* pick_in = (PickPocket_Struct*)app->pBuffer; + + Mob* victim = entity_list.GetMob(pick_in->to); + if (!victim) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + if (victim == this){ + Message(0, "You catch yourself red-handed."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->GetOwnerID()){ + Message(0, "You cannot steal from pets!"); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->IsNPC()){ + victim->CastToNPC()->PickPocket(this); + } + else{ + Message(0, "Stealing from clients not yet supported."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(PopupResponse_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", + sizeof(PopupResponse_Struct), app->size); + DumpPacket(app); + return; + } + PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; + + // Handle any EQEmu defined popup Ids first + switch (prs->popupid) + { + case POPUPID_UPDATE_SHOWSTATSWINDOW: + if (GetTarget() && GetTarget()->IsClient()) + GetTarget()->CastToClient()->SendStatsWindow(this, true); + else + SendStatsWindow(this, true); + return; + + default: + break; + } + + char buf[16]; + sprintf(buf, "%d\0", prs->popupid); + + parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); + + Mob* Target = GetTarget(); + if (Target && Target->IsNPC()) { + parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); + } +} + +void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MovePotionToBelt_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", + sizeof(MovePotionToBelt_Struct), app->size); + DumpPacket(app); + return; + } + MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; + if (mptbs->Action == 0) { + const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); + if (BaseItem) { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; + strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); + database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); + } + } + else { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; + strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); + } +} + +void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint32 aaid = *((uint32 *)app->pBuffer); + + if (aaid >= _maxLeaderAA) + return; + + uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; + if (current_rank >= MAX_LEADERSHIP_TIERS) { + Message(13, "This ability can be trained no further."); + return; + } + + uint8 cost = LeadershipAACosts[aaid][current_rank]; + if (cost == 0) { + Message(13, "This ability can be trained no further."); + return; + } + + //TODO: we need to enforce prerequisits + + if (aaid >= raidAAMarkNPC) { + //it is a raid ability. + if (cost > m_pp.raid_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.raid_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + } + else { + //it is a group ability. + if (cost > m_pp.group_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.group_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); + } + + //success, send them an update + EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); + UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *)outapp->pBuffer; + u->ability_id = aaid; + u->new_rank = m_pp.leader_abilities.ranks[aaid]; + u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities + FastQueuePacket(&outapp); + + Group *g = GetGroup(); + + // Update all group members with the new AA the leader has purchased. + if (g) { + g->UpdateGroupAAs(); + g->SendLeadershipAAUpdate(); + } + +} + +void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends + // further details about the selected player, e.g. Race/Class/AAs/Guild etc. + // + if (app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", + sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); + PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) +{ + // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. + // + // It has a single uint32 payload which is the sort method: + // + // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 + // + if (app->size != sizeof(PVPLeaderBoardRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", + sizeof(PVPLeaderBoardRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); + /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RaidGeneral_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); + DumpPacket(app); + return; + } + + RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; + switch (ri->action) + { + case RaidCommandInviteIntoExisting: + case RaidCommandInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + Group *g = i->GetGroup(); + if (g){ + if (g->IsLeader(i) == false) + Message(13, "You can only invite an ungrouped player or group leader to join your raid."); + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + break; + } + case RaidCommandAcceptInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + if (IsRaidGrouped()){ + i->Message_StringID(0, 5060); //group failed, must invite members not in raid... + return; + } + Raid *r = entity_list.GetRaidByClient(i); + if (r){ + r->VerifyRaid(); + Group *g = GetGroup(); + if (g){ + if (g->GroupCount() + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + else{ + if (1 + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + if (g){//add us all + uint32 freeGroup = r->GetFreeGroup(); + Client *addClient = nullptr; + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + + if (!addClient) + { + addClient = c; + r->SetGroupLeader(addClient->GetName()); + } + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + if (g->IsLeader(g->members[x])) + r->AddMember(c, freeGroup, false, true); + else + r->AddMember(c, freeGroup); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + g->DisbandGroup(); + r->GroupUpdate(freeGroup); + } + else{ + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(this); + r->SendBulkRaid(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + else + { + Group *ig = i->GetGroup(); + Group *g = GetGroup(); + if (g) //if our target has a group + { + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + + uint32 groupFree = r->GetFreeGroup(); //get a free group + if (ig){ //if we already have a group then cycle through adding us... + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]){ + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])){ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else{ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + ig->DisbandGroup(); + r->GroupUpdate(groupFree); + groupFree = r->GetFreeGroup(); + } + else{ //else just add the inviter + r->SendRaidCreate(i); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + } + + Client *addClient = nullptr; + //now add the existing group + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + if (!addClient) + { + if (g->members[x]->IsClient()){ + addClient = g->members[x]->CastToClient(); + r->SetGroupLeader(addClient->GetName()); + } + } + if (g->IsLeader(g->members[x])) + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, false, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + g->DisbandGroup(); + r->GroupUpdate(groupFree); + } + else + { + if (ig){ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]) + { + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])) + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->SendBulkRaid(this); + r->AddMember(this); + ig->DisbandGroup(); + r->GroupUpdate(0); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + else{ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + r->SendRaidCreate(i); + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + r->SendBulkRaid(this); + r->AddMember(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + } + } + break; + } + case RaidCommandDisband: { + Raid *r = entity_list.GetRaidByClient(this); + if (r){ + //if(this == r->GetLeader()){ + uint32 grp = r->GetGroup(ri->leader_name); + + if (grp < 12){ + uint32 i = r->GetPlayerIndex(ri->leader_name); + if (r->members[i].IsGroupLeader){ //assign group leader to someone else + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && i != x){ + if (r->members[x].GroupNumber == grp){ + r->SetGroupLeader(ri->leader_name, false); + r->SetGroupLeader(r->members[x].membername); + break; + } + } + } + + } + if (r->members[i].IsRaidLeader){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) + { + r->SetRaidLeader(r->members[i].membername, r->members[x].membername); + break; + } + } + } + } + + r->RemoveMember(ri->leader_name); + Client *c = entity_list.GetClientByName(ri->leader_name); + if (c) + r->SendGroupDisband(c); + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, grp); + r->GroupUpdate(grp);// break + //} + } + break; + } + case RaidCommandMoveGroup: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (ri->parameter < 12) //moving to a group + { + uint8 grpcount = r->GroupCount(ri->parameter); + + if (grpcount < 6) + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. + break; + + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) + { + r->SetGroupLeader(ri->leader_name, false); + if (oldgrp < 12){ //we were the leader of our old grp + for (int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can + { + if (r->members[x].GroupNumber == oldgrp) + { + if (strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) + { + r->SetGroupLeader(r->members[x].membername); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + } + } + if (grpcount == 0) + r->SetGroupLeader(ri->leader_name); + + r->MoveMember(ri->leader_name, ri->parameter); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + //r->SendGroupUpdate(c); + //break + r->GroupUpdate(ri->parameter); //send group update to our new group + if (oldgrp < 12) //if our old was a group send update there too + r->GroupUpdate(oldgrp); + + //r->SendMakeGroupLeaderPacketAll(); + } + } + else //moving to ungrouped + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ + r->SetGroupLeader(ri->leader_name, false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) + { + r->SetGroupLeader(r->members[x].membername); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + r->MoveMember(ri->leader_name, 0xFFFFFFFF); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + r->GroupUpdate(oldgrp); + //r->SendMakeGroupLeaderPacketAll(); + } + } + break; + } + case RaidCommandRaidLock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (!r->IsLocked()) + r->LockRaid(true); + else + r->SendRaidLockTo(this); + } + break; + } + case RaidCommandRaidUnlock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (r->IsLocked()) + r->LockRaid(false); + else + r->SendRaidUnlockTo(this); + } + break; + } + case RaidCommandLootType2: + case RaidCommandLootType: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Loot type changed to: %d.", ri->parameter); + r->ChangeLootType(ri->parameter); + } + break; + } + + case RaidCommandAddLooter2: + case RaidCommandAddLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Adding %s as a raid looter.", ri->leader_name); + r->AddRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandRemoveLooter2: + case RaidCommandRemoveLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Removing %s as a raid looter.", ri->leader_name); + r->RemoveRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandMakeLeader: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (strcmp(r->leadername, GetName()) == 0){ + r->SetRaidLeader(GetName(), ri->leader_name); + } + } + break; + } + + default: { + Message(13, "Raid command (%d) NYI", ri->action); + break; + } + } +} + +void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RandomReq_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); + return; + } + const RandomReq_Struct* rndq = (const RandomReq_Struct*)app->pBuffer; + uint32 randLow = rndq->low > rndq->high ? rndq->high : rndq->low; + uint32 randHigh = rndq->low > rndq->high ? rndq->low : rndq->high; + uint32 randResult; + + if (randLow == 0 && randHigh == 0) + { // defaults + randLow = 0; + randHigh = 100; + } + randResult = MakeRandomInt(randLow, randHigh); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); + RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; + rr->low = randLow; + rr->high = randHigh; + rr->result = randResult; + strcpy(rr->name, GetName()); + entity_list.QueueCloseClients(this, outapp, false, 400); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BookRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); + return; + } + BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer; + ReadBook(book); + if (GetClientVersion() >= EQClientSoF) + { + EQApplicationPacket EndOfBook(OP_FinishWindow, 0); + QueuePacket(&EndOfBook); + } + return; +} + +void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipeAutoCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", + sizeof(RecipeAutoCombine_Struct), app->size); + return; + } + + RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; + + Object::HandleAutoCombine(this, rac); + return; +} + +void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) +{ + if (app->size < sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", + sizeof(uint32), app->size); + return; + } + uint32 *recipe_id = (uint32*)app->pBuffer; + + SendTradeskillDetails(*recipe_id); + + return; +} + +void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeskillFavorites_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", + sizeof(TradeskillFavorites_Struct), app->size); + return; + } + + TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; + + LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); + + // results show that object_type is combiner type + // some_id = 0 if world combiner, item number otherwise + + // make where clause segment for container(s) + char containers[30]; + if (tsf->some_id == 0) { + // world combiner so no item number + snprintf(containers, 29, "= %u", tsf->object_type); + } + else { + // container in inventory + snprintf(containers, 29, "in (%u,%u)", tsf->object_type, tsf->some_id); + } + + char *query = 0; + char buf[5500]; //gotta be big enough for 500 IDs + + bool first = true; + uint16 r; + char *pos = buf; + + //Assumes item IDs are <10 characters long + for (r = 0; r < 500; r++) { + if (tsf->favorite_recipes[r] == 0) + continue; + + if (first) { + pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); + first = false; + } + else { + pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); + } + } + + if (first) //no favorites.... + return; + + //To be a good kid, I should move this SQL somewhere else... + //but im lazy right now, so it stays here + uint32 qlen = 0; + qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " + " FROM tradeskill_recipe AS tr " + " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + " WHERE tr.enabled <> 0 AND tr.id IN (%s) " + " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " + " GROUP BY tr.id " + " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + " LIMIT 100 ", CharacterID(), buf, containers); + + TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); + + safe_delete_array(query); + return; +} + +void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipesSearch_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", + sizeof(RecipesSearch_Struct), app->size); + return; + } + + RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; + rss->query[55] = '\0'; //just to be sure. + + + LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); + + // make where clause segment for container(s) + char containers[30]; + if (rss->some_id == 0) { + // world combiner so no item number + snprintf(containers, 29, "= %u", rss->object_type); + } + else { + // container in inventory + snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); + } + + char *query = 0; + char searchclause[140]; //2X rss->query + SQL crap + + //omit the rlike clause if query is empty + if (rss->query[0] != 0) { + char buf[120]; //larger than 2X rss->query + database.DoEscapeString(buf, rss->query, strlen(rss->query)); + + snprintf(searchclause, 139, "name rlike '%s' AND", buf); + } + else { + searchclause[0] = '\0'; + } + uint32 qlen = 0; + + //arbitrary limit of 200 recipes, makes sense to me. + qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " + " FROM tradeskill_recipe AS tr " + " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " + " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " + " GROUP BY tr.id " + " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + " LIMIT 200 " + , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); + + TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); + + safe_delete_array(query); + return; +} + +void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) +{ + if (IsInAGuild()) + { + SendGuildRanks(); + SendGuildMembers(); + } + return; +} + +void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + std::set RemovedBuffs; + + if (bbs->Count > 0) + { + std::set::iterator Iterator; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = 0; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x5a; + + for (unsigned int i = 0; i < bbs->Count; ++i) + { + Iterator = BlockedBuffs->find(bbs->SpellID[i]); + + if (Iterator != BlockedBuffs->end()) + { + RemovedBuffs.insert(bbs->SpellID[i]); + + BlockedBuffs->erase(Iterator); + } + } + obbs->Count = RemovedBuffs.size(); + + Iterator = RemovedBuffs.begin(); + + unsigned int Element = 0; + + while (Iterator != RemovedBuffs.end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Report(const EQApplicationPacket *app) +{ + if (!CanUseReport) + { + Message_StringID(MT_System, REPORT_ONCE); + return; + } + + uint32 size = app->size; + uint32 current_point = 0; + std::string reported, reporter; + std::string current_string; + int mode = 0; + + while (current_point < size) + { + if (mode < 2) + { + if (app->pBuffer[current_point] == '|') + { + mode++; + } + else + { + if (mode == 0) + { + reported += app->pBuffer[current_point]; + } + else + { + reporter += app->pBuffer[current_point]; + } + } + current_point++; + } + else + { + if (app->pBuffer[current_point] == 0x0a) + { + current_string += '\n'; + } + else if (app->pBuffer[current_point] == 0x00) + { + CanUseReport = false; + database.AddReport(reporter, reported, current_string); + return; + } + else + { + current_string += app->pBuffer[current_point]; + } + current_point++; + } + } + + CanUseReport = false; + database.AddReport(reporter, reported, current_string); +} + +void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Duel_Struct)) + return; + + EQApplicationPacket* outapp = app->Copy(); + Duel_Struct* ds = (Duel_Struct*)outapp->pBuffer; + uint32 duel = ds->duel_initiator; + ds->duel_initiator = ds->duel_target; + ds->duel_target = duel; + Entity* entity = entity_list.GetID(ds->duel_target); + if (GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { + Message_StringID(10, DUEL_CONSIDERING, entity->GetName()); + return; + } + if (IsDueling()) { + Message_StringID(10, DUEL_INPROGRESS); + return; + } + + if (GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { + SetDuelTarget(ds->duel_target); + entity->CastToClient()->SetDuelTarget(GetID()); + ds->duel_target = ds->duel_initiator; + entity->CastToClient()->FastQueuePacket(&outapp); + entity->CastToClient()->SetDueling(false); + SetDueling(false); + } + else + safe_delete(outapp); + return; +} + +void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) +{ + + EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); + + if (outapp != nullptr) + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player choses which bind to return to. + // The client sends just a 4 byte packet with the selection number in it + // + if (app->size != 4) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", + 4, app->size); + DumpPacket(app); + return; + } + char *Buffer = (char *)app->pBuffer; + + uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + HandleRespawnFromHover(Option); +} + +void Client::Handle_OP_Rewind(const EQApplicationPacket *app) +{ + if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { + Message_StringID(MT_System, REWIND_WAIT); + } + else { + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); + rewind_timer.Start(30000, true); + } +} + +void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); + + const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer; + + _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", + PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); + + _pkt(SPELLS__REZ, app); + + OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); + + if (ra->action == 1) + { + EQApplicationPacket* outapp = app->Copy(); + // Send the OP_RezzComplete to the world server. This finds it's way to the zone that + // the rezzed corpse is in to mark the corpse as rezzed. + outapp->SetOpcode(OP_RezzComplete); + worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Sacrifice_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); + DumpPacket(app); + return; + } + Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; + + if (!PendingSacrifice) { + LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); + DumpPacket(app); + return; + } + + if (ss->Confirm) { + Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); + if (Caster) Sacrifice(Caster); + } + PendingSacrifice = false; + SacrificeCaster.clear(); +} + +void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) +{ + if (HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... + CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up +} + +void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Save(const EQApplicationPacket *app) +{ + // The payload is 192 bytes - Not sure what is contained in payload + Save(); + return; +} + +void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) +{ + Handle_OP_Save(app); +} + +void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //we should enforce being near a real tribute master to change this + //but im not sure how I wanna do that right now. + if (app->size != sizeof(SelectTributeReq_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); + else { + SelectTributeReq_Struct *t = (SelectTributeReq_Struct *)app->pBuffer; + SendTributeDetails(t->client_id, t->tribute_id); + } + return; +} + +void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSenseTraps)) + return; + + if (!p_timers.Expired(&database, pTimerSenseTraps, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = SenseTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerSenseTraps, reuse - 1); + + Trap* trap = entity_list.FindNearbyTrap(this, 800); + + CheckIncreaseSkill(SkillSenseTraps, nullptr); + + if (trap && trap->skill > 0) { + int uskill = GetSkill(SkillSenseTraps); + if ((MakeRandomInt(0, 99) + uskill) >= (MakeRandomInt(0, 99) + trap->skill*0.75)) + { + float xdif = trap->x - GetX(); + float ydif = trap->y - GetY(); + if (xdif == 0 && ydif == 0) + Message(MT_Skills, "You sense a trap right under your feet!"); + else if (xdif > 10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthWest."); + else if (xdif < -10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthEast."); + else if (ydif > 10) + Message(MT_Skills, "You sense a trap to the North."); + else if (xdif > 10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthWest."); + else if (xdif < -10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthEast."); + else if (ydif < -10) + Message(MT_Skills, "You sense a trap to the South."); + else if (xdif > 10) + Message(MT_Skills, "You sense a trap to the West."); + else + Message(MT_Skills, "You sense a trap to the East."); + trap->detected = true; + + float angle = CalculateHeadingToTarget(trap->x, trap->y); + + if (angle < 0) + angle = (256 + angle); + + angle *= 2; + MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); + return; + } + } + Message(MT_Skills, "You did not find any traps nearby."); + return; +} + +void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildMOTD_Struct)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n", app->size, sizeof(GuildMOTD_Struct)); + return; + } + if (!IsInAGuild()) { + Message(13, "You are not in a guild!"); + return; + } + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { + Message(13, "You do not have permissions to edit your guild's MOTD."); + return; + } + + GuildMOTD_Struct* gmotd = (GuildMOTD_Struct*)app->pBuffer; + + mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", + guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); + + if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { + Message(0, "Motd update failed."); + } + + return; +} + +void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_SetServerFilter: got %d, expected %d", app->size, + sizeof(SetServerFilter_Struct)); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) +{ + // if the character has a start city, don't let them use the command + if (m_pp.binds[4].zoneId != 0) { + Message(15, "Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); + return; + } + + if (app->size < 1) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + DumpPacket(app); + return; + } + + float x(0), y(0), z(0); + uint32 zoneid = 0; + uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); + + std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); + return; + } + + bool validCity = false; + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + if (zoneid != startCity) + continue; + + validCity = true; + x = atof(row[2]); + y = atof(row[3]); + z = atof(row[4]); + } + + if (validCity) { + Message(15, "Your home city has been set"); + SetStartZone(startCity, x, y, z); + return; + } + + query = StringFormat("SELECT zone_id, bind_id FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + + Message(15, "Use \"/startcity #\" to choose a home city from the following list:"); + + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + char* name; + database.GetZoneLongName(database.GetZoneName(zoneid), &name); + Message(15, "%d - %s", zoneid, name); + } + +} + +void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetTitle_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); + DumpPacket(app); + return; + } + + SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; + + std::string Title; + + if (!sts->is_suffix) + { + Title = title_manager.GetPrefix(sts->title_id); + SetAATitle(Title.c_str()); + } + else + { + Title = title_manager.GetSuffix(sts->title_id); + SetTitleSuffix(Title.c_str()); + } +} + +void Client::Handle_OP_Shielding(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Shielding_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + return; + } + if (GetClass() != WARRIOR) + { + return; + } + + if (shield_target) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + END_SHIELDING, GetName(), shield_target->GetName()); + for (int y = 0; y < 2; y++) + { + if (shield_target->shielder[y].shielder_id == GetID()) + { + shield_target->shielder[y].shielder_id = 0; + shield_target->shielder[y].shielder_bonus = 0; + } + } + } + Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; + shield_target = entity_list.GetMob(shield->target_id); + bool ack = false; + ItemInst* inst = GetInv().GetItem(MainSecondary); + if (!shield_target) + return; + if (inst) + { + const Item_Struct* shield = inst->GetItem(); + if (shield && shield->ItemType == ItemTypeShield) + { + for (int x = 0; x < 2; x++) + { + if (shield_target->shielder[x].shielder_id == 0) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + START_SHIELDING, GetName(), shield_target->GetName()); + shield_target->shielder[x].shielder_id = GetID(); + int shieldbonus = shield->AC * 2; + switch (GetAA(197)) + { + case 1: + shieldbonus = shieldbonus * 115 / 100; + break; + case 2: + shieldbonus = shieldbonus * 125 / 100; + break; + case 3: + shieldbonus = shieldbonus * 150 / 100; + break; + } + shield_target->shielder[x].shielder_bonus = shieldbonus; + shield_timer.Start(); + ack = true; + break; + } + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + if (!ack) + { + Message_StringID(0, ALREADY_SHIELDED); + shield_target = 0; + return; + } + return; +} + +void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) +{ + EQApplicationPacket empty(OP_ShopEndConfirm); + QueuePacket(&empty); + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); + //outapp->pBuffer[0] = 0x0a; + //outapp->pBuffer[1] = 0x66; + //QueuePacket(outapp); + //safe_delete(outapp); + //Save(); + return; +} + +void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Sell_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", + sizeof(Merchant_Sell_Struct), app->size); + return; + } + RDTSC_Timer t1; + t1.start(); + Merchant_Sell_Struct* mp = (Merchant_Sell_Struct*)app->pBuffer; +#if EQDEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); + DumpPacket(app); +#endif + + int merchantid; + bool tmpmer_used = false; + Mob* tmp = entity_list.GetMob(mp->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + if (mp->quantity < 1) return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + uint32 item_id = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + if (mp->itemslot == ml.slot){ + item_id = ml.item; + break; + } + } + const Item_Struct* item = nullptr; + uint32 prevcharges = 0; + if (item_id == 0) { //check to see if its on the temporary table + std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; + std::list::const_iterator tmp_itr; + TempMerchantList ml; + for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr){ + ml = *tmp_itr; + if (mp->itemslot == ml.slot){ + item_id = ml.item; + tmpmer_used = true; + prevcharges = ml.charges; + break; + } + } + } + item = database.GetItem(item_id); + if (!item){ + //error finding item, client didnt get the update packet for whatever reason, roleplay a tad + Message(15, "%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.", tmp->GetCleanName()); + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueCloseClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + return; + } + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + if (tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) + { + if (prevcharges > item->MaxCharges && item->MaxCharges > 1) + mp->quantity = item->MaxCharges; + else + mp->quantity = prevcharges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); + Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; + mpo->quantity = mp->quantity; + mpo->playerid = mp->playerid; + mpo->npcid = mp->npcid; + mpo->itemslot = mp->itemslot; + + int16 freeslotid = 0; + int16 charges = 0; + if (item->Stackable || item->MaxCharges > 1) + charges = mp->quantity; + else + charges = item->MaxCharges; + + ItemInst* inst = database.CreateItem(item, charges); + + int SinglePrice = 0; + if (RuleB(Merchant, UsePriceMod)) + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); + else + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); + + if (item->MaxCharges > 1) + mpo->price = SinglePrice; + else + mpo->price = SinglePrice * mp->quantity; + if (mpo->price < 0) + { + safe_delete(outapp); + safe_delete(inst); + return; + } + + if (!TakeMoneyFromPP(mpo->price)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", + mpo->quantity, item->ID, item->Name, + mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + safe_delete(outapp); + safe_delete(inst); + return; + } + + bool stacked = TryStacking(inst); + if (!stacked) + freeslotid = m_inv.FindFreeSlot(false, true, item->Size); + + //make sure we are not completely full... + if (freeslotid == MainCursor) { + if (m_inv.GetItem(MainCursor) != nullptr) { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + } + + if (freeslotid == INVALID_INDEX) + { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + + std::string packet; + if (!stacked && inst) { + PutItemInInventory(freeslotid, *inst); + SendItemPacket(freeslotid, inst, ItemPacketTrade); + } + else if (!stacked){ + LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); + } + QueuePacket(outapp); + if (inst && tmpmer_used){ + int32 new_charges = prevcharges - mp->quantity; + zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(), item_id, new_charges); + if (new_charges <= 0){ + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + } + else { + // Update the charges/quantity in the merchant window + inst->SetCharges(new_charges); + inst->SetPrice(SinglePrice); + inst->SetMerchantSlot(mp->itemslot); + inst->SetMerchantCount(new_charges); + + SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); + } + } + safe_delete(inst); + safe_delete(outapp); + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = 0; + qsaudit->merchant_money.gold = 0; + qsaudit->merchant_money.silver = 0; + qsaudit->merchant_money.copper = 0; + qsaudit->merchant_count = 1; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = (mpo->price / 1000); + qsaudit->char_money.gold = (mpo->price / 100) % 10; + qsaudit->char_money.silver = (mpo->price / 10) % 10; + qsaudit->char_money.copper = mpo->price % 10; + qsaudit->char_count = 0; + + qsaudit->items[0].char_slot = freeslotid; + qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); + qsaudit->items[0].charges = mpo->quantity; + qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + if (RuleB(EventLog, RecordBuyFromMerchant)) + LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); + + if ((RuleB(Character, EnableDiscoveredItems))) + { + if (!GetGM() && !IsDiscovered(item_id)) + DiscoverItem(item_id); + } + + t1.stop(); + std::cout << "At 1: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Purchase_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", + sizeof(Merchant_Purchase_Struct), app->size); + return; + } + RDTSC_Timer t1(true); + Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(mp->npcid); + + if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + return; + + uint32 price = 0; + uint32 itemid = GetItemIDAt(mp->itemslot); + if (itemid == 0) + return; + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(mp->itemslot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item.."); + return; + } + if (mp->quantity > 1) + { + if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) + return; + } + + if (!item->NoDrop) { + //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); + return; + } + + int cost_quantity = mp->quantity; + if (inst->IsCharged()) + int cost_quantity = 1; + + if (RuleB(Merchant, UsePriceMod)) + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price + else + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5); + AddMoneyToPP(price, false); + + if (inst->IsStackable() || inst->IsCharged()) + { + unsigned int i_quan = inst->GetCharges(); + if (mp->quantity > i_quan || inst->IsCharged()) + mp->quantity = i_quan; + } + else + mp->quantity = 1; + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, mp->quantity, price, item, false); + + int charges = mp->quantity; + //Hack workaround so usable items with 0 charges aren't simply deleted + if (charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) + charges = 1; + + int freeslot = 0; + if (charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(), itemid, charges, true)) > 0){ + ItemInst* inst2 = inst->Clone(); + if (RuleB(Merchant, UsePriceMod)){ + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor, false)); + } + else + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); + inst2->SetMerchantSlot(freeslot); + + uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); + + if (inst2->IsStackable()) { + inst2->SetCharges(MerchantQuantity); + } + inst2->SetMerchantCount(MerchantQuantity); + + SendItemPacket(freeslot - 1, inst2, ItemPacketMerchant); + safe_delete(inst2); + } + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = (price / 1000); + qsaudit->merchant_money.gold = (price / 100) % 10; + qsaudit->merchant_money.silver = (price / 10) % 10; + qsaudit->merchant_money.copper = price % 10; + qsaudit->merchant_count = 0; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = 0; + qsaudit->char_money.gold = 0; + qsaudit->char_money.silver = 0; + qsaudit->char_money.copper = 0; + qsaudit->char_count = 1; + + qsaudit->items[0].char_slot = mp->itemslot; + qsaudit->items[0].item_id = itemid; + qsaudit->items[0].charges = charges; + qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + // Now remove the item from the player, this happens regardless of outcome + if (!inst->IsStackable()) + this->DeleteItemInInventory(mp->itemslot, 0, false); + else + this->DeleteItemInInventory(mp->itemslot, mp->quantity, false); + + //This forces the price to show up correctly for charged items. + if (inst->IsCharged()) + mp->quantity = 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); + Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer; + mco->npcid = vendor->GetID(); + mco->itemslot = mp->itemslot; + mco->quantity = mp->quantity; + mco->price = price; + QueuePacket(outapp); + safe_delete(outapp); + SendMoneyUpdate(); + t1.start(); + Save(1); + t1.stop(); + std::cout << "Save took: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Click_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); + return; + } + + Merchant_Click_Struct* mc = (Merchant_Click_Struct*)app->pBuffer; + + // Send back opcode OP_ShopRequest - tells client to open merchant window. + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; + int merchantid = 0; + Mob* tmp = entity_list.GetMob(mc->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + int action = 1; + if (merchantid == 0) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = 1; //open... + mco->rate = 1.0; + QueuePacket(outapp); + safe_delete(outapp); + return; + } + if (tmp->IsEngaged()){ + this->Message_StringID(0, MERCHANT_BUSY); + action = 0; + } + if (GetFeigned() || IsInvisible()) + { + Message(0, "You cannot use a merchant right now."); + action = 0; + } + int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); + int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); + if (factionlvl >= 7) { + MerchantRejectMessage(tmp, primaryfaction); + action = 0; + } + + if (tmp->Charmed()) + action = 0; + + // 1199 I don't have time for that now. etc + if (!tmp->CastToNPC()->IsMerchantOpen()) { + tmp->Say_StringID(MakeRandomInt(1199, 1202)); + action = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = action; // Merchant command 0x01 = open + if (RuleB(Merchant, UsePriceMod)){ + mco->rate = 1 / ((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp, true)); // works + } + else + mco->rate = 1 / (RuleR(Merchant, BuyCostMod)); + + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + if (action == 1) + BulkSendMerchantInventory(merchantid, tmp->GetNPCTypeID()); + + return; +} + +void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { + return; //You cannot sneak if you do not have sneak + } + + if (!p_timers.Expired(&database, pTimerSneak, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerSneak, SneakReuseTime - 1); + + bool was = sneaking; + if (sneaking){ + sneaking = false; + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + else { + CheckIncreaseSkill(SkillSneak, nullptr, 5); + } + float hidechance = ((GetSkill(SkillSneak) / 300.0f) + .25) * 100; + float random = MakeRandomFloat(0, 99); + if (!was && random < hidechance) { + sneaking = true; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x0F; + sa_out->parameter = sneaking; + QueuePacket(outapp); + safe_delete(outapp); + if (GetClass() == ROGUE){ + outapp = new EQApplicationPacket(OP_SimpleMessage, 12); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + if (sneaking){ + msg->string_id = 347; + } + else { + msg->string_id = 348; + } + FastQueuePacket(&outapp); + } + return; +} + +void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpawnAppearance_Struct)) { + std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + + if (sa->spawn_id != GetID()) + return; + + if (sa->type == AT_Invis) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + if (GetClientVersion() < EQClientSoF) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + } + return; + } + invisible = false; + hidden = false; + improved_hidden = false; + entity_list.QueueClients(this, app, true); + return; + } + else if (sa->type == AT_Anim) { + if (IsAIControlled()) + return; + if (sa->parameter == ANIM_STAND) { + SetAppearance(eaStanding); + playeraction = 0; + SetFeigned(false); + BindWound(this, false, true); + camp_timer.Disable(); + } + else if (sa->parameter == ANIM_SIT) { + SetAppearance(eaSitting); + playeraction = 1; + if (!UseBardSpellLogic()) + InterruptSpell(); + SetFeigned(false); + BindWound(this, false, true); + } + else if (sa->parameter == ANIM_CROUCH) { + if (!UseBardSpellLogic()) + InterruptSpell(); + SetAppearance(eaCrouching); + playeraction = 2; + SetFeigned(false); + } + else if (sa->parameter == ANIM_DEATH) { // feign death too + SetAppearance(eaDead); + playeraction = 3; + InterruptSpell(); + } + else if (sa->parameter == ANIM_LOOT) { + SetAppearance(eaLooting); + playeraction = 4; + SetFeigned(false); + } + + // This is from old code + // I have no clue what it's for + /* + else if (sa->parameter == 0x05) { + // Illusion + std::cout << "Illusion packet recv'd:" << std::endl; + DumpPacket(app); + } + */ + else { + std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; + return; + } + + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Anon) { + // For Anon/Roleplay + if (sa->parameter == 1) { // Anon + m_pp.anon = 1; + } + else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp + m_pp.anon = 2; + } + else if (sa->parameter == 0) { // This is Non-Anon + m_pp.anon = 0; + } + else { + std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; + return; + } + entity_list.QueueClients(this, app, true); + UpdateWho(); + } + else if ((sa->type == AT_HP) && (dead == 0)) { + return; + } + else if (sa->type == AT_AFK) { + this->AFK = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Split) { + m_pp.autosplit = (sa->parameter == 1); + } + else if (sa->type == AT_Sneak) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillSneak)) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + return; + } + this->sneaking = 0; + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Size) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) + { + entity_list.QueueClients(this, app, false); + } + else if (sa->type == AT_Levitate) + { + // don't do anything with this, we tell the client when it's + // levitating, not the other way around + } + else if (sa->type == AT_ShowHelm) + { + m_pp.showhelm = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else { + std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec + << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + } + return; +} + +void Client::Handle_OP_Split(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Split_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); + return; + } + // The client removes the money on its own, but we have to + // update our state anyway, and make sure they had enough to begin + // with. + Split_Struct *split = (Split_Struct *)app->pBuffer; + //Per the note above, Im not exactly sure what to do on error + //to notify the client of the error... + if (!isgrouped) { + Message(13, "You can not split money if your not in a group."); + return; + } + Group *cgroup = GetGroup(); + if (cgroup == nullptr) { + //invalid group, not sure if we should say more... + Message(13, "You can not split money if your not in a group."); + return; + } + + if (!TakeMoneyFromPP(static_cast(split->copper) + + 10 * static_cast(split->silver) + + 100 * static_cast(split->gold) + + 1000 * static_cast(split->platinum))) { + Message(13, "You do not have enough money to do that split."); + return; + } + cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); + + return; + +} + +void Client::Handle_OP_Surname(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Surname_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + return; + } + + if (!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) + { + Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); + return; + } + + if (GetLevel() < 20) + { + Message_StringID(15, SURNAME_LEVEL); + return; + } + + Surname_Struct* surname = (Surname_Struct*)app->pBuffer; + + char *c = nullptr; + bool first = true; + for (c = surname->lastname; *c; c++) + { + if (first) + { + *c = toupper(*c); + first = false; + } + else + { + *c = tolower(*c); + } + } + + if (strlen(surname->lastname) >= 20) { + Message_StringID(15, SURNAME_TOO_LONG); + return; + } + + if (!database.CheckNameFilter(surname->lastname, true)) + { + Message_StringID(15, SURNAME_REJECTED); + return; + } + + ChangeLastName(surname->lastname); + p_timers.Start(pTimerSurnameChange, 604800); + + EQApplicationPacket* outapp = app->Copy(); + outapp = app->Copy(); + surname = (Surname_Struct*)outapp->pBuffer; + surname->unknown0064 = 1; + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(SwapSpell_Struct)) { + std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; + return; + } + const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*)app->pBuffer; + int swapspelltemp; + + if (swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) + return; + + swapspelltemp = m_pp.spell_book[swapspell->from_slot]; + if (swapspelltemp < 0){ + return; + } + m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; + m_pp.spell_book[swapspell->to_slot] = swapspelltemp; + + database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); + + QueuePacket(app); + return; +} + +void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + return; + } + + if (GetTarget()) + { + GetTarget()->IsTargeted(-1); + } + + // Locate and cache new target + ClientTarget_Struct* ct = (ClientTarget_Struct*)app->pBuffer; + pClientSideTarget = ct->new_target; + if (!IsAIControlled()) + { + Mob *nt = entity_list.GetMob(ct->new_target); + if (nt) + { + SetTarget(nt); + if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || + (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || + (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) + nt->SendBuffsToClient(this); + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(0); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(0); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(0); + + return; + } + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + return; + } + + // HoTT + if (GetTarget() && GetTarget()->GetTarget()) + { + SetHoTT(GetTarget()->GetTarget()->GetID()); + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + } + else + { + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + } + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(GetTarget()); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(GetTarget()); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(GetTarget()); + + // For /target, send reject or success packet + if (app->GetOpcode() == OP_TargetCommand) { + if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { + if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + //Targeting something we shouldn't with /target + //but the client allows this without MQ so you don't flag it + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + QueuePacket(app); + EQApplicationPacket hp_app; + GetTarget()->IsTargeted(1); + GetTarget()->CreateHPPacket(&hp_app); + QueuePacket(&hp_app, false); + } + else + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + } + } + else + { + if (GetTarget()) + { + if (GetGM()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsAssistExempted()) + { + GetTarget()->IsTargeted(1); + SetAssistExemption(false); + return; + } + else if (GetTarget()->IsClient()) + { + //make sure this client is in our raid/group + GetTarget()->IsTargeted(1); + return; + } + else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", + GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget((Mob*)nullptr); + return; + } + else if (IsPortExempted()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsSenseExempted()) + { + GetTarget()->IsTargeted(1); + SetSenseExemption(false); + return; + } + else if (IsXTarget(GetTarget())) + { + GetTarget()->IsTargeted(1); + return; + } + else if (GetBindSightTarget()) + { + if (GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + } + } + else if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + + GetTarget()->IsTargeted(1); + } + } + return; +} + +void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +{ + Handle_OP_TargetCommand(app); +} + +void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(TaskHistoryRequest_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", + sizeof(TaskHistoryRequest_Struct), app->size); + DumpPacket(app); + return; + } + TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->SendTaskHistory(this, ths->TaskIndex); +} + +void Client::Handle_OP_Taunt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: " << sizeof(ClientTarget_Struct) << std::endl; + return; + } + + if (!p_timers.Expired(&database, pTimerTaunt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerTaunt, TauntReuseTime - 1); + + if (GetTarget() == nullptr || !GetTarget()->IsNPC()) + return; + + Taunt(GetTarget()->CastToNPC(), false); + return; +} + +void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_TGB(const EQApplicationPacket *app) +{ + OPTGB(app); + return; +} + +void Client::Handle_OP_Track(const EQApplicationPacket *app) +{ + if (GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) + return; + + if (GetSkill(SkillTracking) == 0) + SetSkill(SkillTracking, 1); + else + CheckIncreaseSkill(SkillTracking, nullptr, 15); + + if (!entity_list.MakeTrackPacket(this)) + LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); + + return; +} + +void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) +{ + int PlayerClass = GetClass(); + + if ((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) + return; + + if (app->size != sizeof(TrackTarget_Struct)) + { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", + sizeof(TrackTarget_Struct), app->size); + return; + } + + TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; + + TrackingID = tts->EntityID; +} + +void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) +{ + // size 0 send right after OP_Track + return; +} + +void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) +{ + Mob* with = trade->With(); + trade->state = TradeAccepted; + + if (with && with->IsClient()) { + //finish trade... + // Have both accepted? + Client* other = with->CastToClient(); + other->QueuePacket(app); + + if (other->trade->state == trade->state) { + other->trade->state = TradeCompleting; + trade->state = TradeCompleting; + + // should we do this for NoDrop items as well? + if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { + Message_StringID(13, TRADE_CANCEL_LORE); + other->Message_StringID(13, TRADE_CANCEL_LORE); + this->FinishTrade(this); + other->FinishTrade(other); + other->trade->Reset(); + trade->Reset(); + } + else { + // Audit trade to database for both trade streams + other->trade->LogTrade(); + trade->LogTrade(); + + // start QS code + if (RuleB(QueryServ, PlayerLogTrades)) { + QSPlayerLogTrade_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); + + // Perform actual trade + this->FinishTrade(other, true, &event_entry, &event_details); + other->FinishTrade(this, false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); + QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSTradeItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + // end QS code + } + else { + this->FinishTrade(other); + other->FinishTrade(this); + } + + other->trade->Reset(); + trade->Reset(); + } + // All done + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + other->QueuePacket(outapp); + this->FastQueuePacket(&outapp); + } + } + // Trading with a Mob object that is not a Client. + else if (with) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + QueuePacket(outapp); + safe_delete(outapp); + if (with->IsNPC()) { + // Audit trade to database for player trade stream + if (RuleB(QueryServ, PlayerLogHandins)) { + QSPlayerLogHandin_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); + + FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); + QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSHandinItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + } + else { + FinishTrade(with->CastToNPC()); + } + } +#ifdef BOTS + // TODO: Log Bot trades + else if (with->IsBot()) + with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); +#endif + trade->Reset(); + } + + + return; +} + +void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeBusy_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); + return; + } + // Trade request recipient is cancelling the trade due to being busy + // Trade requester gets message "I'm busy right now" + // Send busy message on to trade initiator if client + TradeBusy_Struct* msg = (TradeBusy_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_Trader(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. + // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. + + _pkt(TRADING__PACKETS, app); + + uint32 max_items = 80; + + /* + if (GetClientVersion() >= EQClientRoF) + max_items = 200; + */ + + //Show Items + if (app->size == sizeof(Trader_ShowItems_Struct)) + { + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; + + switch (sis->Code) + { + case BazaarTrader_EndTraderMode: { + Trader_EndTrader(); + break; + } + case BazaarTrader_EndTransaction: { + + Client* c = entity_list.GetClientByID(sis->TraderID); + if (c) + c->WithCustomer(0); + else + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + + break; + } + case BazaarTrader_ShowItems: { + Trader_ShowItems(); + break; + } + default: { + _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); + break; + } + } + } + else if (app->size == sizeof(ClickTrader_Struct)) + { + if (Buyer) { + Trader_EndTrader(); + Message(13, "You cannot be a Trader and Buyer at the same time."); + return; + } + + ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; + + if (ints->Code == BazaarTrader_StartTraderMode) + { + GetItems_Struct* gis = GetTraderItems(); + + // Verify there are no NODROP or items with a zero price + bool TradeItemsValid = true; + + for (uint32 i = 0; i < max_items; i++) { + + if (gis->Items[i] == 0) break; + + if (ints->ItemCost[i] == 0) { + Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + const Item_Struct *Item = database.GetItem(gis->Items[i]); + + if (!Item) { + Message(13, "Unexpected error. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + + if (Item->NoDrop == 0) { + Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + } + + if (!TradeItemsValid) { + Trader_EndTrader(); + return; + } + + for (uint32 i = 0; i < max_items; i++) { + if (database.GetItem(gis->Items[i])) { + database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i], + gis->Charges[i], ints->ItemCost[i], i); + } + else { + //return; //sony doesnt memset so assume done on first bad item + break; + } + + } + safe_delete(gis); + + this->Trader_StartTrader(); + + if (GetClientVersion() >= EQClientRoF) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); + TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; + tss->Code = BazaarTrader_StartTraderMode2; + QueuePacket(outapp); + _pkt(TRADING__PACKETS, outapp); + safe_delete(outapp); + } + } + else if (app->size == sizeof(TraderStatus_Struct)) + { + TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; + + if (tss->Code == BazaarTrader_ShowItems) + { + Trader_ShowItems(); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", + ints->Code); + + LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); + } + } + + else if (app->size == sizeof(TraderPriceUpdate_Struct)) + { + HandleTraderPriceUpdate(app); + } + else { + _log(TRADING__CLIENT, "Unknown size for OP_Trader: %i\n", app->size); + LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); + DumpPacket(app); + return; + } + + return; +} + +void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // Client has elected to buy an item from a Trader + // + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(TraderBuy_Struct)){ + + TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; + + if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){ + + BuyTraderItem(tbs, Trader, app); + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); + + } + return; +} + +void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Client requesting a trade session from an npc/client + // Trade session not started until OP_TradeRequestAck is sent + + BreakInvis(); + + // Pass trade request on to recipient + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } +#ifndef BOTS + else if (tradee && tradee->IsNPC()) { +#else + else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { +#endif + //npcs always accept + trade->Start(msg->to_mob_id); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); + TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; + acc->from_mob_id = msg->to_mob_id; + acc->to_mob_id = msg->from_mob_id; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Trade request recipient is acknowledging they are able to trade + // After this, the trade session has officially started + // Send ack on to trade initiator if client + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + trade->Start(msg->to_mob_id); + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // This is when a potential purchaser right clicks on this client who is in Trader mode to + // browse their goods. + // + _pkt(TRADING__PACKETS, app); + + TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; + + if (app->size != sizeof(TraderClick_Struct)) { + + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); + + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); + + TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; + + Client* Customer = entity_list.GetClientByID(tcs->TraderID); + + if (Customer) + outtcs->Approval = Customer->WithCustomer(GetID()); + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" + " returned a nullptr pointer"); + return; + } + + outtcs->TraderID = tcs->TraderID; + + outtcs->Unknown008 = 0x3f800000; + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + if (outtcs->Approval) { + this->BulkSendTraderInventory(Customer->CharacterID()); + Customer->Trader_CustomerBrowsing(this); + } + else + Message_StringID(clientMessageYellow, TRADER_BUSY); + + safe_delete(outapp); + + return; +} + +void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(NewCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", + sizeof(NewCombine_Struct), app->size); + return; + } + /*if (m_tradeskill_object == nullptr) { + Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); + return; + }*/ + + //fixed this to work for non-world objects + + // Delegate to tradeskill object to perform combine + NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; + Object::HandleCombine(this, in_combine, m_tradeskill_object); + return; +} + +void Client::Handle_OP_Translocate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Translocate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); + DumpPacket(app); + return; + } + Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; + + if (!PendingTranslocate) return; + + if ((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { + Message(13, "You did not accept the Translocate within the required time limit."); + PendingTranslocate = false; + return; + } + + if (its->Complete == 1) { + + int SpellID = PendingTranslocateData.SpellID; + int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); + + if (i == 0) + { + // If the spell has a translocate to bind effect, AND we are already in the zone the client + // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself + // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are + // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. + if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && + zone->GetZoneID() == PendingTranslocateData.ZoneID) + { + PendingTranslocate = false; + GoToBind(); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); + Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; + memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); + + //Was sending the packet back to initiate client zone... + //but that could be abusable, so lets go through proper channels + MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); + } + } + + PendingTranslocate = false; +} + +void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates an item... + if (app->size != sizeof(TributeItem_Struct)) + printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeItem(t->slot, t->quantity); + + _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates money + if (app->size != sizeof(TributeMoney_Struct)) + printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeMoney(t->platinum); + + _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + return; +} + +void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(uint32)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); + else { + uint32 *val = (uint32 *)app->pBuffer; + ToggleTribute(*val ? true : false); + } + return; +} + +void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //sent when the client changes their tribute settings... + if (app->size != sizeof(TributeInfo_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); + else { + TributeInfo_Struct *t = (TributeInfo_Struct *)app->pBuffer; + ChangeTributeSettings(t); + } + return; +} + +void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(VeteranClaimRequest)) + { + LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", + app->size, sizeof(VeteranClaimRequest)); + DumpPacket(app); + return; + } + + VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; + + if (vcr->claim_id == 0xFFFFFFFF) //request update packet + { + SendRewards(); + } + else //try to claim something! + { + if (!TryReward(vcr->claim_id)) + { + Message(13, "Your claim has been rejected."); + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = -1; + FastQueuePacket(&vetapp); + } + else + { + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = 0; + FastQueuePacket(&vetapp); + } + } +} + +void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(VoiceMacroIn_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", + sizeof(VoiceMacroIn_Struct), app->size); + + DumpPacket(app); + + return; + } + + if (!RuleB(Chat, EnableVoiceMacros)) return; + + VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; + + VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); + +} + +void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(WearChange_Struct)) { + std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + return; + } + + WearChange_Struct* wc = (WearChange_Struct*)app->pBuffer; + if (wc->spawn_id != GetID()) + return; + + // we could maybe ignore this and just send our own from moveitem + entity_list.QueueClients(this, app, true); + return; +} + +void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Who_All_Struct)) { + std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; + return; + } + Who_All_Struct* whoall = (Who_All_Struct*)app->pBuffer; + + if (whoall->type == 0) // SoF only, for regular /who + entity_list.ZoneWho(this, whoall); + else + WhoAll(whoall); + return; +} + +void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +{ + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + DumpPacket(app); + return; + } + + XTargetAutoAddHaters = app->ReadUInt8(0); +} + +void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +{ + if (app->size < 12) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + DumpPacket(app); + return; + } + + uint32 Unknown000 = app->ReadUInt32(0); + + if (Unknown000 != 1) + return; + + uint32 Slot = app->ReadUInt32(4); + + if (Slot >= XTARGET_HARDCAP) + return; + + XTargetType Type = (XTargetType)app->ReadUInt32(8); + + XTargets[Slot].Type = Type; + XTargets[Slot].ID = 0; + XTargets[Slot].Name[0] = 0; + + switch (Type) + { + case Empty: + case Auto: + { + break; + } + + case CurrentTargetPC: + { + char Name[65]; + + app->ReadString(Name, 12, 64); + Client *c = entity_list.GetClientByName(Name); + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, Name, 64); + } + SendXTargetPacket(Slot, c); + + break; + } + + case CurrentTargetNPC: + { + char Name[65]; + app->ReadString(Name, 12, 64); + Mob *m = entity_list.GetMob(Name); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + break; + } + } + + case TargetsTarget: + { + if (GetTarget()) + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + else + UpdateXTargetType(TargetsTarget, nullptr); + + break; + } + + case GroupTank: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainTankName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + case GroupTankTarget: + { + Group *g = GetGroup(); + + if (g) + g->NotifyTankTarget(this); + + break; + } + + case GroupAssist: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainAssistName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case GroupAssistTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyAssistTarget(this); + + break; + } + + case Puller: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetPullerName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case PullerTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyPullerTarget(this); + + break; + } + + case GroupMarkTarget1: + case GroupMarkTarget2: + case GroupMarkTarget3: + { + Group *g = GetGroup(); + + if (g) + g->SendMarkedNPCsToMember(this); + + break; + } + + case RaidAssist1: + case RaidAssist2: + case RaidAssist3: + case RaidAssist1Target: + case RaidAssist2Target: + case RaidAssist3Target: + case RaidMarkTarget1: + case RaidMarkTarget2: + case RaidMarkTarget3: + { + // Not implemented yet. + break; + } + + case MyPet: + { + Mob *m = GetPet(); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + case MyPetTarget: + { + Mob *m = GetPet(); + + if (m) + m = m->GetTarget(); + + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + + default: + LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); + break; + } + +} + +void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); + *(uint32 *)outapp->pBuffer = GetID(); + entity_list.QueueCloseClients(this, outapp, true, 100.0); + safe_delete(outapp); + return; +} + +/* +void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) +{ + return; +} +*/ diff --git a/zone/client_packet.h b/zone/client_packet.h index 424a8f78f..7cf7ce06b 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -1,292 +1,294 @@ - void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); - void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); - void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); - void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + // connecting opcode handlers void Handle_Connect_0x3e33(const EQApplicationPacket *app); + void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); void Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app); + void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); void Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app); + void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); + void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_Connect_OP_TGB(const EQApplicationPacket *app); + void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); + void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); void Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app); void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app); - void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); - void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); - void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); - void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); - void Handle_Connect_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_OP_AutoAttack(const EQApplicationPacket *app); - void Handle_OP_AutoAttack2(const EQApplicationPacket *app); - void Handle_OP_Consent(const EQApplicationPacket *app); - void Handle_OP_ConsentDeny(const EQApplicationPacket *app); - void Handle_OP_TargetMouse(const EQApplicationPacket *app); - void Handle_OP_TargetCommand(const EQApplicationPacket *app); - void Handle_OP_Shielding(const EQApplicationPacket *app); - void Handle_OP_Jump(const EQApplicationPacket *app); - void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureRequest(const EQApplicationPacket *app); - void Handle_OP_LDoNButton(const EQApplicationPacket *app); - void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); - void Handle_OP_Consume(const EQApplicationPacket *app); - void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); - void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); - void Handle_OP_Consider(const EQApplicationPacket *app); - void Handle_OP_Begging(const EQApplicationPacket *app); - void Handle_OP_TestBuff(const EQApplicationPacket *app); - void Handle_OP_Surname(const EQApplicationPacket *app); - void Handle_OP_ClearSurname(const EQApplicationPacket *app); - void Handle_OP_YellForHelp(const EQApplicationPacket *app); - void Handle_OP_Assist(const EQApplicationPacket *app); - void Handle_OP_AssistGroup(const EQApplicationPacket *app); - void Handle_OP_GMTraining(const EQApplicationPacket *app); - void Handle_OP_GMEndTraining(const EQApplicationPacket *app); - void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); - void Handle_OP_DuelResponse(const EQApplicationPacket *app); - void Handle_OP_DuelResponse2(const EQApplicationPacket *app); - void Handle_OP_RequestDuel(const EQApplicationPacket *app); - void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_OP_BazaarInspect(const EQApplicationPacket *app); - void Handle_OP_Death(const EQApplicationPacket *app); - void Handle_OP_MoveCoin(const EQApplicationPacket *app); - void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); - void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); - void Handle_OP_MoveItem(const EQApplicationPacket *app); - void Handle_OP_Camp(const EQApplicationPacket *app); - void Handle_OP_Logout(const EQApplicationPacket *app); - void Handle_OP_SenseHeading(const EQApplicationPacket *app); - void Handle_OP_LDoNOpen(const EQApplicationPacket *app); - void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNInspect(const EQApplicationPacket *app); - void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); - void Handle_OP_FeignDeath(const EQApplicationPacket *app); - void Handle_OP_Sneak(const EQApplicationPacket *app); - void Handle_OP_Hide(const EQApplicationPacket *app); - void Handle_OP_ChannelMessage(const EQApplicationPacket *app); - void Handle_OP_WearChange(const EQApplicationPacket *app); - void Handle_OP_ZoneChange(const EQApplicationPacket *app); - void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); - void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); - void Handle_OP_Save(const EQApplicationPacket *app); - void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); - void Handle_OP_EndLootRequest(const EQApplicationPacket *app); - void Handle_OP_LootRequest(const EQApplicationPacket *app); - void Handle_OP_Dye(const EQApplicationPacket *app); - void Handle_OP_LootItem(const EQApplicationPacket *app); - void Handle_OP_GuildDelete(const EQApplicationPacket *app); - void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); - void Handle_OP_GetGuildsList(const EQApplicationPacket *app); - void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildPeace(const EQApplicationPacket *app); - void Handle_OP_GuildWar(const EQApplicationPacket *app); - void Handle_OP_GuildLeader(const EQApplicationPacket *app); - void Handle_OP_GuildDemote(const EQApplicationPacket *app); - void Handle_OP_GuildPromote(const EQApplicationPacket *app); - void Handle_OP_GuildInvite(const EQApplicationPacket *app); - void Handle_OP_GuildRemove(const EQApplicationPacket *app); - void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); - void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); - void Handle_OP_ManaChange(const EQApplicationPacket *app); - void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); - void Handle_OP_SwapSpell(const EQApplicationPacket *app); - void Handle_OP_CastSpell(const EQApplicationPacket *app); - void Handle_OP_DeleteItem(const EQApplicationPacket *app); - void Handle_OP_CombatAbility(const EQApplicationPacket *app); - void Handle_OP_Taunt(const EQApplicationPacket *app); - void Handle_OP_InstillDoubt(const EQApplicationPacket *app); - void Handle_OP_RezzAnswer(const EQApplicationPacket *app); - void Handle_OP_GMSummon(const EQApplicationPacket *app); - void Handle_OP_TradeRequest(const EQApplicationPacket *app); - void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); - void Handle_OP_CancelTrade(const EQApplicationPacket *app); - void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); - void Handle_OP_BoardBoat(const EQApplicationPacket *app); - void Handle_OP_LeaveBoat(const EQApplicationPacket *app); - void Handle_OP_RandomReq(const EQApplicationPacket *app); - void Handle_OP_Buff(const EQApplicationPacket *app); - void Handle_OP_GMHideMe(const EQApplicationPacket *app); - void Handle_OP_GMNameChange(const EQApplicationPacket *app); - void Handle_OP_GMKill(const EQApplicationPacket *app); - void Handle_OP_GMLastName(const EQApplicationPacket *app); - void Handle_OP_GMToggle(const EQApplicationPacket *app); - void Handle_OP_LFGCommand(const EQApplicationPacket *app); - void Handle_OP_GMGoto(const EQApplicationPacket *app); - void Handle_OP_TraderShop(const EQApplicationPacket *app); - void Handle_OP_ShopRequest(const EQApplicationPacket *app); - void Handle_OP_BazaarSearch(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); - void Handle_OP_ShopEnd(const EQApplicationPacket *app); -// void Handle_OP_CloseContainer(const EQApplicationPacket *app); - void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); - void Handle_OP_ClickObject(const EQApplicationPacket *app); - void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); - void Handle_OP_RecipesSearch(const EQApplicationPacket *app); - void Handle_OP_RecipeDetails(const EQApplicationPacket *app); - void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); - void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); - void Handle_OP_ItemName(const EQApplicationPacket *app); - void Handle_OP_AugmentItem(const EQApplicationPacket *app); - void Handle_OP_ClickDoor(const EQApplicationPacket *app); - void Handle_OP_CreateObject(const EQApplicationPacket *app); - void Handle_OP_FaceChange(const EQApplicationPacket *app); - void Handle_OP_GroupInvite(const EQApplicationPacket *app); - void Handle_OP_GroupInvite2(const EQApplicationPacket *app); - void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); - void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); - void Handle_OP_GroupFollow(const EQApplicationPacket *app); - void Handle_OP_GroupFollow2(const EQApplicationPacket *app); - void Handle_OP_GroupDisband(const EQApplicationPacket *app); - void Handle_OP_GroupDelete(const EQApplicationPacket *app); - void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); - void Handle_OP_InspectRequest(const EQApplicationPacket *app); - void Handle_OP_InspectAnswer(const EQApplicationPacket *app); - void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); - void Handle_OP_Medding(const EQApplicationPacket *app); - void Handle_OP_DeleteSpell(const EQApplicationPacket *app); - void Handle_OP_PetitionBug(const EQApplicationPacket *app); - void Handle_OP_Bug(const EQApplicationPacket *app); - void Handle_OP_Petition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); - void Handle_OP_PetitionResolve(const EQApplicationPacket *app); - void Handle_OP_PetitionDelete(const EQApplicationPacket *app); - void Handle_OP_PetCommands(const EQApplicationPacket *app); - void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionQue(const EQApplicationPacket *app); - void Handle_OP_PDeletePetition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); - void Handle_OP_ReadBook(const EQApplicationPacket *app); - void Handle_OP_Emote(const EQApplicationPacket *app); - void Handle_OP_Animation(const EQApplicationPacket *app); - void Handle_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); - void Handle_OP_GMKick(const EQApplicationPacket *app); - void Handle_OP_GMServers(const EQApplicationPacket *app); - void Handle_OP_Illusion(const EQApplicationPacket *app); - void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); - void Handle_OP_Fishing(const EQApplicationPacket *app); - void Handle_OP_Forage(const EQApplicationPacket *app); - void Handle_OP_Mend(const EQApplicationPacket *app); - void Handle_OP_EnvDamage(const EQApplicationPacket *app); - void Handle_OP_Damage(const EQApplicationPacket *app); - void Handle_OP_AAAction(const EQApplicationPacket *app); - void Handle_OP_TraderBuy(const EQApplicationPacket *app); - void Handle_OP_Trader(const EQApplicationPacket *app); - void Handle_OP_GMFind(const EQApplicationPacket *app); - void Handle_OP_PickPocket(const EQApplicationPacket *app); - void Handle_OP_Bind_Wound(const EQApplicationPacket *app); - void Handle_OP_TrackTarget(const EQApplicationPacket *app); - void Handle_OP_Track(const EQApplicationPacket *app); - void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); + // connected opcode handlers void Handle_0x0193(const EQApplicationPacket *app); void Handle_0x01e7(const EQApplicationPacket *app); - void Handle_OP_ClientError(const EQApplicationPacket *app); - void Handle_OP_ReloadUI(const EQApplicationPacket *app); - void Handle_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_Split(const EQApplicationPacket *app); - void Handle_OP_SenseTraps(const EQApplicationPacket *app); - void Handle_OP_DisarmTraps(const EQApplicationPacket *app); - void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); - void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); - void Handle_OP_TributeItem(const EQApplicationPacket *app); - void Handle_OP_TributeMoney(const EQApplicationPacket *app); - void Handle_OP_SelectTribute(const EQApplicationPacket *app); - void Handle_OP_TributeUpdate(const EQApplicationPacket *app); - void Handle_OP_TributeToggle(const EQApplicationPacket *app); - void Handle_OP_TributeNPC(const EQApplicationPacket *app); - void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); - void Handle_OP_CrashDump(const EQApplicationPacket *app); - void Handle_OP_ControlBoat(const EQApplicationPacket *app); - void Handle_OP_DumpName(const EQApplicationPacket *app); - void Handle_OP_SetRunMode(const EQApplicationPacket *app); - void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); - void Handle_OP_Heartbeat(const EQApplicationPacket *app); - void Handle_OP_SafePoint(const EQApplicationPacket *app); - void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); - void Handle_OP_BankerChange(const EQApplicationPacket *app); - void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); - void Handle_OP_SetTitle(const EQApplicationPacket *app); - void Handle_OP_RequestTitles(const EQApplicationPacket *app); - void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); - void Handle_OP_Ignore(const EQApplicationPacket *app); - void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); - void Handle_OP_AutoFire(const EQApplicationPacket *app); - void Handle_OP_Rewind(const EQApplicationPacket *app); - void Handle_OP_RaidCommand(const EQApplicationPacket *app); - void Handle_OP_Translocate(const EQApplicationPacket *app); - void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_AAAction(const EQApplicationPacket *app); void Handle_OP_AcceptNewTask(const EQApplicationPacket *app); - void Handle_OP_CancelTask(const EQApplicationPacket *app); - void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); - void Handle_OP_KeyRing(const EQApplicationPacket *app); - void Handle_OP_FriendsWho(const EQApplicationPacket *app); - void Handle_OP_Bandolier(const EQApplicationPacket *app); - void Handle_OP_PopupResponse(const EQApplicationPacket *app); - void Handle_OP_PotionBelt(const EQApplicationPacket *app); - void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_LFPCommand(const EQApplicationPacket *app); - void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_Barter(const EQApplicationPacket *app); - void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); - void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); - void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); - void Handle_OP_DelegateAbility(const EQApplicationPacket *app); - void Handle_OP_ApplyPoison(const EQApplicationPacket *app); - void Handle_OP_AugmentInfo(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); - void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app); - void Handle_OP_RespawnWindow(const EQApplicationPacket *app); - void Handle_OP_GroupUpdate(const EQApplicationPacket *app); - void Handle_OP_SetStartCity(const EQApplicationPacket *app); - void Handle_OP_Report(const EQApplicationPacket *app); - void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); - void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); - void Handle_OP_GuildBank(const EQApplicationPacket *app); - void Handle_OP_GroupRoles(const EQApplicationPacket *app); - void Handle_OP_HideCorpse(const EQApplicationPacket *app); - void Handle_OP_TradeBusy(const EQApplicationPacket *app); - void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); - void Handle_OP_GuildStatus(const EQApplicationPacket *app); - void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); - void Handle_OP_CorpseDrag(const EQApplicationPacket *app); - void Handle_OP_CorpseDrop(const EQApplicationPacket *app); - void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); - void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); + void Handle_OP_AdventureRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app); void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app); void Handle_OP_AltCurrencySell(const EQApplicationPacket *app); - void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); - void Handle_OP_CrystalCreate(const EQApplicationPacket *app); - void Handle_OP_LFGuild(const EQApplicationPacket *app); - void Handle_OP_XTargetRequest(const EQApplicationPacket *app); - void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); - void Handle_OP_ItemPreview(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryHire(const EQApplicationPacket *app); - void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); - void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); - void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); - void Handle_OP_OpenInventory(const EQApplicationPacket *app); - void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); + void Handle_OP_Animation(const EQApplicationPacket *app); + void Handle_OP_ApplyPoison(const EQApplicationPacket *app); + void Handle_OP_Assist(const EQApplicationPacket *app); + void Handle_OP_AssistGroup(const EQApplicationPacket *app); + void Handle_OP_AugmentInfo(const EQApplicationPacket *app); + void Handle_OP_AugmentItem(const EQApplicationPacket *app); + void Handle_OP_AutoAttack(const EQApplicationPacket *app); + void Handle_OP_AutoAttack2(const EQApplicationPacket *app); + void Handle_OP_AutoFire(const EQApplicationPacket *app); + void Handle_OP_Bandolier(const EQApplicationPacket *app); + void Handle_OP_BankerChange(const EQApplicationPacket *app); + void Handle_OP_Barter(const EQApplicationPacket *app); + void Handle_OP_BazaarInspect(const EQApplicationPacket *app); + void Handle_OP_BazaarSearch(const EQApplicationPacket *app); + void Handle_OP_Begging(const EQApplicationPacket *app); + void Handle_OP_Bind_Wound(const EQApplicationPacket *app); + void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_BoardBoat(const EQApplicationPacket *app); + void Handle_OP_Buff(const EQApplicationPacket *app); + void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); + void Handle_OP_Bug(const EQApplicationPacket *app); + void Handle_OP_Camp(const EQApplicationPacket *app); + void Handle_OP_CancelTask(const EQApplicationPacket *app); + void Handle_OP_CancelTrade(const EQApplicationPacket *app); + void Handle_OP_CastSpell(const EQApplicationPacket *app); + void Handle_OP_ChannelMessage(const EQApplicationPacket *app); + void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); + void Handle_OP_ClearSurname(const EQApplicationPacket *app); + void Handle_OP_ClickDoor(const EQApplicationPacket *app); + void Handle_OP_ClickObject(const EQApplicationPacket *app); + void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); + void Handle_OP_ClientError(const EQApplicationPacket *app); void Handle_OP_ClientTimeStamp(const EQApplicationPacket *app); + void Handle_OP_ClientUpdate(const EQApplicationPacket *app); +// void Handle_OP_CloseContainer(const EQApplicationPacket *app); + void Handle_OP_CombatAbility(const EQApplicationPacket *app); + void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); + void Handle_OP_Consent(const EQApplicationPacket *app); + void Handle_OP_ConsentDeny(const EQApplicationPacket *app); + void Handle_OP_Consider(const EQApplicationPacket *app); + void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); + void Handle_OP_Consume(const EQApplicationPacket *app); + void Handle_OP_ControlBoat(const EQApplicationPacket *app); + void Handle_OP_CorpseDrag(const EQApplicationPacket *app); + void Handle_OP_CorpseDrop(const EQApplicationPacket *app); + void Handle_OP_CrashDump(const EQApplicationPacket *app); + void Handle_OP_CreateObject(const EQApplicationPacket *app); + void Handle_OP_CrystalCreate(const EQApplicationPacket *app); + void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); + void Handle_OP_Damage(const EQApplicationPacket *app); + void Handle_OP_Death(const EQApplicationPacket *app); + void Handle_OP_DelegateAbility(const EQApplicationPacket *app); + void Handle_OP_DeleteItem(const EQApplicationPacket *app); + void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); + void Handle_OP_DeleteSpell(const EQApplicationPacket *app); + void Handle_OP_DisarmTraps(const EQApplicationPacket *app); + void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); + void Handle_OP_DuelResponse(const EQApplicationPacket *app); + void Handle_OP_DuelResponse2(const EQApplicationPacket *app); + void Handle_OP_DumpName(const EQApplicationPacket *app); + void Handle_OP_Dye(const EQApplicationPacket *app); + void Handle_OP_Emote(const EQApplicationPacket *app); + void Handle_OP_EndLootRequest(const EQApplicationPacket *app); + void Handle_OP_EnvDamage(const EQApplicationPacket *app); + void Handle_OP_FaceChange(const EQApplicationPacket *app); + void Handle_OP_FeignDeath(const EQApplicationPacket *app); + void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); + void Handle_OP_Fishing(const EQApplicationPacket *app); + void Handle_OP_Forage(const EQApplicationPacket *app); + void Handle_OP_FriendsWho(const EQApplicationPacket *app); + void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_GetGuildsList(const EQApplicationPacket *app); + void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); + void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); + void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); + void Handle_OP_GMEndTraining(const EQApplicationPacket *app); + void Handle_OP_GMFind(const EQApplicationPacket *app); + void Handle_OP_GMGoto(const EQApplicationPacket *app); + void Handle_OP_GMHideMe(const EQApplicationPacket *app); + void Handle_OP_GMKick(const EQApplicationPacket *app); + void Handle_OP_GMKill(const EQApplicationPacket *app); + void Handle_OP_GMLastName(const EQApplicationPacket *app); + void Handle_OP_GMNameChange(const EQApplicationPacket *app); + void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); + void Handle_OP_GMServers(const EQApplicationPacket *app); + void Handle_OP_GMSummon(const EQApplicationPacket *app); + void Handle_OP_GMToggle(const EQApplicationPacket *app); + void Handle_OP_GMTraining(const EQApplicationPacket *app); + void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); + void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); + void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); + void Handle_OP_GroupDelete(const EQApplicationPacket *app); + void Handle_OP_GroupDisband(const EQApplicationPacket *app); + void Handle_OP_GroupFollow(const EQApplicationPacket *app); + void Handle_OP_GroupFollow2(const EQApplicationPacket *app); + void Handle_OP_GroupInvite(const EQApplicationPacket *app); + void Handle_OP_GroupInvite2(const EQApplicationPacket *app); + void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); + void Handle_OP_GroupRoles(const EQApplicationPacket *app); + void Handle_OP_GroupUpdate(const EQApplicationPacket *app); + void Handle_OP_GuildBank(const EQApplicationPacket *app); + void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_GuildDelete(const EQApplicationPacket *app); + void Handle_OP_GuildDemote(const EQApplicationPacket *app); + void Handle_OP_GuildInvite(const EQApplicationPacket *app); + void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); + void Handle_OP_GuildLeader(const EQApplicationPacket *app); + void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); + void Handle_OP_GuildPeace(const EQApplicationPacket *app); + void Handle_OP_GuildPromote(const EQApplicationPacket *app); + void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); + void Handle_OP_GuildRemove(const EQApplicationPacket *app); + void Handle_OP_GuildStatus(const EQApplicationPacket *app); + void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); + void Handle_OP_GuildWar(const EQApplicationPacket *app); + void Handle_OP_Heartbeat(const EQApplicationPacket *app); + void Handle_OP_Hide(const EQApplicationPacket *app); + void Handle_OP_HideCorpse(const EQApplicationPacket *app); + void Handle_OP_Ignore(const EQApplicationPacket *app); + void Handle_OP_Illusion(const EQApplicationPacket *app); + void Handle_OP_InspectAnswer(const EQApplicationPacket *app); + void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); + void Handle_OP_InspectRequest(const EQApplicationPacket *app); + void Handle_OP_InstillDoubt(const EQApplicationPacket *app); + void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); + void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); + void Handle_OP_ItemName(const EQApplicationPacket *app); + void Handle_OP_ItemPreview(const EQApplicationPacket *app); + void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); + void Handle_OP_Jump(const EQApplicationPacket *app); + void Handle_OP_KeyRing(const EQApplicationPacket *app); + void Handle_OP_LDoNButton(const EQApplicationPacket *app); + void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); + void Handle_OP_LDoNInspect(const EQApplicationPacket *app); + void Handle_OP_LDoNOpen(const EQApplicationPacket *app); + void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); + void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); + void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); + void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); + void Handle_OP_LeaveBoat(const EQApplicationPacket *app); + void Handle_OP_LFGCommand(const EQApplicationPacket *app); + void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LFGuild(const EQApplicationPacket *app); + void Handle_OP_LFPCommand(const EQApplicationPacket *app); + void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); + void Handle_OP_Logout(const EQApplicationPacket *app); + void Handle_OP_LootItem(const EQApplicationPacket *app); + void Handle_OP_LootRequest(const EQApplicationPacket *app); + void Handle_OP_ManaChange(const EQApplicationPacket *app); + void Handle_OP_Medding(const EQApplicationPacket *app); + void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); + void Handle_OP_Mend(const EQApplicationPacket *app); + void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); + void Handle_OP_MercenaryHire(const EQApplicationPacket *app); + void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); + void Handle_OP_MoveCoin(const EQApplicationPacket *app); + void Handle_OP_MoveItem(const EQApplicationPacket *app); + void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); + void Handle_OP_OpenInventory(const EQApplicationPacket *app); + void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); + void Handle_OP_PDeletePetition(const EQApplicationPacket *app); + void Handle_OP_PetCommands(const EQApplicationPacket *app); + void Handle_OP_Petition(const EQApplicationPacket *app); + void Handle_OP_PetitionBug(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); + void Handle_OP_PetitionDelete(const EQApplicationPacket *app); + void Handle_OP_PetitionQue(const EQApplicationPacket *app); + void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); + void Handle_OP_PetitionResolve(const EQApplicationPacket *app); + void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); + void Handle_OP_PickPocket(const EQApplicationPacket *app); + void Handle_OP_PopupResponse(const EQApplicationPacket *app); + void Handle_OP_PotionBelt(const EQApplicationPacket *app); + void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); + void Handle_OP_RaidCommand(const EQApplicationPacket *app); + void Handle_OP_RandomReq(const EQApplicationPacket *app); + void Handle_OP_ReadBook(const EQApplicationPacket *app); + void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); + void Handle_OP_RecipeDetails(const EQApplicationPacket *app); + void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); + void Handle_OP_RecipesSearch(const EQApplicationPacket *app); + void Handle_OP_ReloadUI(const EQApplicationPacket *app); + void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_Report(const EQApplicationPacket *app); + void Handle_OP_RequestDuel(const EQApplicationPacket *app); + void Handle_OP_RequestTitles(const EQApplicationPacket *app); + void Handle_OP_RespawnWindow(const EQApplicationPacket *app); + void Handle_OP_Rewind(const EQApplicationPacket *app); + void Handle_OP_RezzAnswer(const EQApplicationPacket *app); + void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); + void Handle_OP_SafePoint(const EQApplicationPacket *app); + void Handle_OP_Save(const EQApplicationPacket *app); + void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); + void Handle_OP_SelectTribute(const EQApplicationPacket *app); + void Handle_OP_SenseHeading(const EQApplicationPacket *app); + void Handle_OP_SenseTraps(const EQApplicationPacket *app); + void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_SetRunMode(const EQApplicationPacket *app); + void Handle_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_OP_SetStartCity(const EQApplicationPacket *app); + void Handle_OP_SetTitle(const EQApplicationPacket *app); + void Handle_OP_Shielding(const EQApplicationPacket *app); + void Handle_OP_ShopEnd(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); + void Handle_OP_ShopRequest(const EQApplicationPacket *app); + void Handle_OP_Sneak(const EQApplicationPacket *app); + void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_OP_Split(const EQApplicationPacket *app); + void Handle_OP_Surname(const EQApplicationPacket *app); + void Handle_OP_SwapSpell(const EQApplicationPacket *app); + void Handle_OP_TargetCommand(const EQApplicationPacket *app); + void Handle_OP_TargetMouse(const EQApplicationPacket *app); + void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); + void Handle_OP_Taunt(const EQApplicationPacket *app); + void Handle_OP_TestBuff(const EQApplicationPacket *app); + void Handle_OP_TGB(const EQApplicationPacket *app); + void Handle_OP_Track(const EQApplicationPacket *app); + void Handle_OP_TrackTarget(const EQApplicationPacket *app); + void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); + void Handle_OP_TradeBusy(const EQApplicationPacket *app); + void Handle_OP_Trader(const EQApplicationPacket *app); + void Handle_OP_TraderBuy(const EQApplicationPacket *app); + void Handle_OP_TradeRequest(const EQApplicationPacket *app); + void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); + void Handle_OP_TraderShop(const EQApplicationPacket *app); + void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); + void Handle_OP_Translocate(const EQApplicationPacket *app); + void Handle_OP_TributeItem(const EQApplicationPacket *app); + void Handle_OP_TributeMoney(const EQApplicationPacket *app); + void Handle_OP_TributeNPC(const EQApplicationPacket *app); + void Handle_OP_TributeToggle(const EQApplicationPacket *app); + void Handle_OP_TributeUpdate(const EQApplicationPacket *app); + void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); + void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); + void Handle_OP_WearChange(const EQApplicationPacket *app); + void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); + void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); + void Handle_OP_XTargetRequest(const EQApplicationPacket *app); + void Handle_OP_YellForHelp(const EQApplicationPacket *app); + void Handle_OP_ZoneChange(const EQApplicationPacket *app); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index ea761b9b2..deff0f0f2 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -14,14 +14,31 @@ Lua_Packet::Lua_Packet(int opcode, int size) { owned_ = true; } +Lua_Packet::Lua_Packet(int opcode, int size, bool raw) { + if(raw) { + SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size)); + owned_ = true; + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(opcode); + } else { + SetLuaPtrData(new EQApplicationPacket(static_cast(opcode), size)); + owned_ = true; + } +} + Lua_Packet& Lua_Packet::operator=(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -33,10 +50,14 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -54,6 +75,16 @@ int Lua_Packet::GetOpcode() { } void Lua_Packet::SetOpcode(int op) { + Lua_Safe_Call_Void(); + self->SetOpcodeBypass(static_cast(op)); +} + +int Lua_Packet::GetRawOpcode() { + Lua_Safe_Call_Int(); + return static_cast(self->GetOpcodeBypass()); +} + +void Lua_Packet::SetRawOpcode(int op) { Lua_Safe_Call_Void(); self->SetOpcode(static_cast(op)); } @@ -244,11 +275,14 @@ luabind::scope lua_register_packet() { return luabind::class_("Packet") .def(luabind::constructor<>()) .def(luabind::constructor()) + .def(luabind::constructor()) .property("null", &Lua_Packet::Null) .property("valid", &Lua_Packet::Valid) .def("GetSize", &Lua_Packet::GetSize) .def("GetOpcode", &Lua_Packet::GetOpcode) .def("SetOpcode", &Lua_Packet::SetOpcode) + .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) + .def("SetRawOpcode", &Lua_Packet::SetRawOpcode) .def("WriteInt8", &Lua_Packet::WriteInt8) .def("WriteInt16", &Lua_Packet::WriteInt16) .def("WriteInt32", &Lua_Packet::WriteInt32) @@ -412,7 +446,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("YellForHelp", static_cast(OP_YellForHelp)), luabind::value("SafePoint", static_cast(OP_SafePoint)), luabind::value("Buff", static_cast(OP_Buff)), - luabind::value("BuffFadeMsg", static_cast(OP_BuffFadeMsg)), + luabind::value("ColoredText", static_cast(OP_ColoredText)), luabind::value("SpecialMesg", static_cast(OP_SpecialMesg)), luabind::value("Consent", static_cast(OP_Consent)), luabind::value("ConsentResponse", static_cast(OP_ConsentResponse)), @@ -809,7 +843,10 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("MercenaryDismiss", static_cast(OP_MercenaryDismiss)), luabind::value("MercenaryTimerRequest", static_cast(OP_MercenaryTimerRequest)), luabind::value("OpenInventory", static_cast(OP_OpenInventory)), - luabind::value("OpenContainer", static_cast(OP_OpenContainer)) + luabind::value("OpenContainer", static_cast(OP_OpenContainer)), + luabind::value("Marquee", static_cast(OP_Marquee)), + luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), + luabind::value("GuildPromote", static_cast(OP_GuildPromote)) ]; } diff --git a/zone/lua_packet.h b/zone/lua_packet.h index c036594d1..a3c5ac553 100644 --- a/zone/lua_packet.h +++ b/zone/lua_packet.h @@ -21,6 +21,7 @@ public: Lua_Packet() : Lua_Ptr(nullptr), owned_(false) { } Lua_Packet(EQApplicationPacket *d) : Lua_Ptr(d), owned_(false) { } Lua_Packet(int opcode, int size); + Lua_Packet(int opcode, int size, bool raw); Lua_Packet& operator=(const Lua_Packet& o); Lua_Packet(const Lua_Packet& o); virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } } @@ -28,6 +29,8 @@ public: int GetSize(); int GetOpcode(); void SetOpcode(int op); + int GetRawOpcode(); + void SetRawOpcode(int op); void WriteInt8(int offset, int value); void WriteInt16(int offset, int value); void WriteInt32(int offset, int value); diff --git a/zone/mob.h b/zone/mob.h index 0d365fdac..cf6a032e5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -526,7 +526,7 @@ public: //More stuff to sort: - virtual bool IsRaidTarget() { return false; }; + virtual bool IsRaidTarget() const { return false; }; virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false); bool IsTargeted() const { return (targeted > 0); } inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;} diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b017a90b0..9d7a93a7b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2860,7 +2860,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_HealRate: case SE_SkillDamageTaken: case SE_FcSpellVulnerability: - case SE_SpellTrigger: case SE_FcTwincast: case SE_DelayDeath: case SE_CastOnFadeEffect: diff --git a/zone/spells.cpp b/zone/spells.cpp index 5d60b11a2..a18eabe20 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4720,8 +4720,8 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) if(send_message) { const char *fadetext = spells[spell_id].spell_fades; - outapp = new EQApplicationPacket(OP_BuffFadeMsg, sizeof(BuffFadeMsg_Struct) + strlen(fadetext)); - BuffFadeMsg_Struct *bfm = (BuffFadeMsg_Struct *) outapp->pBuffer; + outapp = new EQApplicationPacket(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext)); + ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer; bfm->color = MT_Spells; memcpy(bfm->msg, fadetext, strlen(fadetext)); QueuePacket(outapp); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9a7ac54aa..3728e3144 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1034,10 +1034,13 @@ bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_S "disc_id " "FROM " "`character_disciplines`" - "WHERE `id` = %u ORDER BY `disc_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Disciplines */ + memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES)); for (auto row = results.begin(); row != results.end(); ++row) { - if (i < MAX_PP_DISCIPLINES){ + if (i < MAX_PP_DISCIPLINES){ pp->disciplines.values[i] = atoi(row[0]); } i++; @@ -1148,14 +1151,19 @@ bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Str bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); - auto results = database.QueryDatabase(query); int i = 0; + auto results = database.QueryDatabase(query); + int i = 0; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - pp->tributes[i].tribute = 0; + pp->tributes[i].tribute = 0xFFFFFFFF; pp->tributes[i].tier = 0; } + i = 0; for (auto row = results.begin(); row != results.end(); ++row) { - pp->tributes[i].tier = atoi(row[0]); - pp->tributes[i].tribute = atoi(row[1]); + if(atoi(row[1]) != TRIBUTE_NONE){ + pp->tributes[i].tier = atoi(row[0]); + pp->tributes[i].tribute = atoi(row[1]); + i++; + } } return true; } @@ -1239,21 +1247,24 @@ bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint } bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ - std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); auto results = QueryDatabase(query); + std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); + auto results = QueryDatabase(query); LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterDisc", query); return true; } bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); + std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); + QueryDatabase(query); /* Save Tributes only if we have values... */ for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffu){ - std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != TRIBUTE_NONE){ + std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + QueryDatabase(query); LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } - } + } return true; }